mirror of
https://github.com/Polprzewodnikowy/SummerCart64.git
synced 2025-01-13 04:59:09 +01:00
shuffle some bits
This commit is contained in:
parent
598a4205bb
commit
9f9c3fc19c
@ -3,8 +3,8 @@
|
||||
- [Address decoding limitations](#address-decoding-limitations)
|
||||
- [Flash mapped sections](#flash-mapped-sections)
|
||||
- [SC64 registers](#sc64-registers)
|
||||
- [`0x1FFF_0000`: **STATUS/COMMAND**](#0x1fff_0000-statuscommand)
|
||||
- [`0x1FFF_0004`: **DATA0** and `0x1FFF_0008`: **DATA1**](#0x1fff_0004-data0-and-0x1fff_0008-data1)
|
||||
- [`0x1FFF_0000`: **SCR**](#0x1fff_0000-scr)
|
||||
- [`0x1FFF_0004`: **DATA0** / `0x1FFF_0008`: **DATA1**](#0x1fff_0004-data0--0x1fff_0008-data1)
|
||||
- [`0x1FFF_000C`: **IDENTIFIER**](#0x1fff_000c-identifier)
|
||||
- [`0x1FFF_0010`: **KEY**](#0x1fff_0010-key)
|
||||
- [`0x1FFF_0014`: **IRQ**](#0x1fff_0014-irq)
|
||||
@ -13,9 +13,9 @@
|
||||
- [Without interrupt](#without-interrupt)
|
||||
- [With interrupt](#with-interrupt)
|
||||
|
||||
---
|
||||
|
||||
## Internal memory map
|
||||
|
||||
# Internal memory map
|
||||
|
||||
This mapping is used internally by FPGA/μC and when accessing flashcart from USB side.
|
||||
|
||||
@ -33,9 +33,9 @@ This mapping is used internally by FPGA/μC and when accessing flashcart from US
|
||||
- Note [2]: Due to BlockRAM usage optimization this section is read only.
|
||||
- Note [3]: Read returns `0`. Maximum accessible address space is 128 MiB.
|
||||
|
||||
---
|
||||
|
||||
## PI memory map
|
||||
|
||||
# PI memory map
|
||||
|
||||
This mapping is used when accessing flashcart from N64 side.
|
||||
|
||||
@ -84,37 +84,41 @@ Special commands are provided for performing flash erase and program.
|
||||
During those operations avoid accessing flash mapped sections.
|
||||
Data read will be corrupted and erase/program operations slows down.
|
||||
|
||||
---
|
||||
|
||||
## SC64 registers
|
||||
|
||||
# SC64 registers
|
||||
|
||||
SC64 contains small register region used for communication between N64 and controller code running on the μC.
|
||||
Protocol is command based with support for up to 256 different commands and two 32-bit argument/result values per operation.
|
||||
Support for interrupts is provided but currently no command relies on it, 64DD IRQ is handled separately.
|
||||
Command execution finish can be optionally signaled with "cart interrupt"
|
||||
|
||||
| name | address | size | access | usage |
|
||||
| ------------------ | ------------- | ------- | ------ | -------------------------------- |
|
||||
| **STATUS/COMMAND** | `0x1FFF_0000` | 4 bytes | RW | Command execution and status |
|
||||
| -------------- | ------------- | ------- | ------ | -------------------------------- |
|
||||
| **SCR** | `0x1FFF_0000` | 4 bytes | RW | Command execution and status |
|
||||
| **DATA0** | `0x1FFF_0004` | 4 bytes | RW | Command argument/result 0 |
|
||||
| **DATA1** | `0x1FFF_0008` | 4 bytes | RW | Command argument/result 1 |
|
||||
| **IDENTIFIER** | `0x1FFF_000C` | 4 bytes | RW | Flashcart identifier |
|
||||
| **KEY** | `0x1FFF_0010` | 4 bytes | W | SC64 register access lock/unlock |
|
||||
| **IRQ** | `0x1FFF_0014` | 4 bytes | RW | IRQ clear and enable |
|
||||
| **IRQ** | `0x1FFF_0014` | 4 bytes | W | IRQ clear and enable |
|
||||
| **AUX** | `0x1FFF_0018` | 4 bytes | RW | Auxiliary interrupt data channel |
|
||||
|
||||
---
|
||||
|
||||
#### `0x1FFF_0000`: **STATUS/COMMAND**
|
||||
### `0x1FFF_0000`: **SCR**
|
||||
|
||||
| name | bits | access | meaning |
|
||||
| ----------------- | ------ | ------ | ----------------------------------------------------------- |
|
||||
| ----------------- | ------ | ------ | ------------------------------------------------------------ |
|
||||
| `CMD_BUSY` | [31] | R | `1` if dispatched command is pending/executing |
|
||||
| `CMD_ERROR` | [30] | R | `1` if last executed command returned with error code |
|
||||
| `BTN_IRQ_PENDING` | [29] | R | `1` if flashcart has raised a "button pressed" interrupt |
|
||||
| `CMD_IRQ_PENDING` | [28] | R | `1` if flashcart has raised a "command finish" interrupt |
|
||||
| `USB_IRQ_PENDING` | [27] | R | `1` if flashcart has raised au "USB not empty" interrupt |
|
||||
| `AUX_IRQ_PENDING` | [26] | R | `1` if flashcart has raised an "AUX not empty" interrupt |
|
||||
| N/A | [25:9] | N/A | Unused, write `0` for future compatibility |
|
||||
| `BTN_IRQ_MASK` | [28] | R | `1` means "button pressed" interrupt is enabled (always `1`) |
|
||||
| `CMD_IRQ_PENDING` | [27] | R | `1` if flashcart has raised a "command finish" interrupt |
|
||||
| `CMD_IRQ_MASK` | [26] | R | `1` means "command finish" interrupt is enabled (always `1`) |
|
||||
| `USB_IRQ_PENDING` | [25] | R | `1` if flashcart has raised an "USB not empty" interrupt |
|
||||
| `USB_IRQ_MASK` | [24] | R | `1` means "USB not empty" interrupt is enabled |
|
||||
| `AUX_IRQ_PENDING` | [23] | R | `1` if flashcart has raised an "AUX not empty" interrupt |
|
||||
| `AUX_IRQ_MASK` | [22] | R | `1` means "AUX not empty" interrupt is enabled |
|
||||
| N/A | [21:9] | N/A | Unused, write `0` for future compatibility |
|
||||
| `CMD_IRQ_REQUEST` | [8] | RW | Raise cart interrupt signal when command finishes execution |
|
||||
| `CMD_ID` | [7:0] | RW | Command ID to be executed |
|
||||
|
||||
@ -122,17 +126,18 @@ Note: Write to this register raises `CMD_BUSY` bit and clears `CMD_ERROR` bit. F
|
||||
|
||||
---
|
||||
|
||||
#### `0x1FFF_0004`: **DATA0** and `0x1FFF_0008`: **DATA1**
|
||||
### `0x1FFF_0004`: **DATA0** / `0x1FFF_0008`: **DATA1**
|
||||
|
||||
| name | bits | access | meaning |
|
||||
| --------- | ------ | ------ | ---------------------------------- |
|
||||
| `DATA0/1` | [31:0] | RW | Command argument (W) or result (R) |
|
||||
|
||||
Note: Result is valid only when command has executed and `CMD_BUSY` bit is reset. When `CMD_ERROR` is set then **DATA0** register contains error code.
|
||||
Note: Result is valid only when command has finished execution and `CMD_BUSY` bit is reset.
|
||||
When `CMD_ERROR` is set then **DATA0** register contains error code.
|
||||
|
||||
---
|
||||
|
||||
#### `0x1FFF_000C`: **IDENTIFIER**
|
||||
### `0x1FFF_000C`: **IDENTIFIER**
|
||||
|
||||
| name | bits | access | meaning |
|
||||
| ------------ | ------ | ------ | ----------------------------------- |
|
||||
@ -141,7 +146,7 @@ Note: Result is valid only when command has executed and `CMD_BUSY` bit is reset
|
||||
|
||||
---
|
||||
|
||||
#### `0x1FFF_0010`: **KEY**
|
||||
### `0x1FFF_0010`: **KEY**
|
||||
|
||||
| name | bits | access | meaning |
|
||||
| ----- | ------ | ------ | ------------------------------------------ |
|
||||
@ -156,20 +161,15 @@ Value `0xFFFFFFFF` will lock all SC64 registers if flashcart is in unlock state.
|
||||
|
||||
---
|
||||
|
||||
#### `0x1FFF_0014`: **IRQ**
|
||||
### `0x1FFF_0014`: **IRQ**
|
||||
|
||||
| name | bits | access | meaning |
|
||||
| ----------------- | ------- | ------ | ----------------------------------------------------------------- |
|
||||
| ----------------- | ------- | ------ | ---------------------------------------------- |
|
||||
| `BTN_CLEAR` | [31] | W | Write `1` to clear "button pressed" interrupt |
|
||||
| `CMD_CLEAR` | [30] | W | Write `1` to clear "command finish" interrupt |
|
||||
| `USB_CLEAR` | [29] | W | Write `1` to clear "USB not empty" interrupt |
|
||||
| `AUX_CLEAR` | [28] | W | Write `1` to clear "AUX not empty" interrupt |
|
||||
| N/A | [27:24] | N/A | Unused, write `0` for future compatibility |
|
||||
| `MCU_IRQ_MASK` | [23] | R | `1` means "button pressed" interrupt is enabled (it's always `1`) |
|
||||
| `CMD_IRQ_MASK` | [22] | R | `1` means "command finish" interrupt is enabled (it's always `1`) |
|
||||
| `USB_IRQ_MASK` | [21] | R | `1` means "USB not empty" interrupt is enabled |
|
||||
| `AUX_IRQ_MASK` | [20] | R | `1` means "AUX not empty" interrupt is enabled |
|
||||
| N/A | [19:16] | N/A | Unused, write `0` for future compatibility |
|
||||
| N/A | [27:12] | N/A | Unused, write `0` for future compatibility |
|
||||
| `USB_IRQ_DISABLE` | [11] | W | Write `1` to disable "USB not empty" interrupt |
|
||||
| `USB_IRQ_ENABLE` | [10] | W | Write `1` to enable "USB not empty" interrupt |
|
||||
| `AUX_IRQ_DISABLE` | [9] | W | Write `1` to disable "AUX not empty" interrupt |
|
||||
@ -179,17 +179,19 @@ Value `0xFFFFFFFF` will lock all SC64 registers if flashcart is in unlock state.
|
||||
Note: All interrupts are cleared and disabled when any of the following events occur:
|
||||
- Hard reset
|
||||
- NMI reset
|
||||
- SC64 registers locking
|
||||
- SC64 registers lock
|
||||
|
||||
SC64 interrupts are completely disabled when register access is not enabled.
|
||||
|
||||
---
|
||||
|
||||
#### `0x1FFF_0018`: **AUX**
|
||||
### `0x1FFF_0018`: **AUX**
|
||||
|
||||
This register can be used as a very simple interface to the PC via USB.
|
||||
Writing to this register generates an USB transfer with the contents of the written data.
|
||||
New data available in the register is signaled via cart interrupt that needs to be enabled beforehand in the `IRQ` register.
|
||||
New data available in the register are signaled via cart interrupt that needs to be enabled beforehand by setting `AUX_IRQ_ENABLE` bit in the **IRQ** register.
|
||||
Status can be also manually polled by checking `AUX_IRQ_PENDING` bit in **SCR** register.
|
||||
Interrupt needs to be acknowledged by setting `AUX_CLEAR` bit in the **IRQ** register.
|
||||
There is no flow control, use this register as a ping-pong interface.
|
||||
For example, PC sends AUX data, N64 receives interrupt, reads the data then writes to this register with a response.
|
||||
This flow can be reversed if needed - N64 can be the initiating side.
|
||||
@ -198,28 +200,39 @@ This flow can be reversed if needed - N64 can be the initiating side.
|
||||
| ------ | ------ | ------ | -------------- |
|
||||
| `DATA` | [31:0] | RW | Arbitrary data |
|
||||
|
||||
---
|
||||
This register is used by the upload process in the `sc64deployer` to notify running app on the N64 about certain events.
|
||||
All `DATA` values with upper 8 bits set to `1` (`0xFFxxxxxx`) are reserved for internal use by the SC64.
|
||||
Refrain from using these values in your app for uses other than listed below.
|
||||
Currently defined reserved `DATA` values are:
|
||||
|
||||
## Command execution flow
|
||||
- `0xFF000001` - **IO Halt** - causes the running app to stop all cartridge IO activity (PI bus and Joybus) in preparation for uploading new ROM to the SC64.
|
||||
App still should listen to the AUX interrupt and respond to other messages.
|
||||
- `0xFF000002` - **Reboot** - causes the running app to perform soft reset by reloading IPL3 from the ROM and start executing it.
|
||||
|
||||
App running on the N64 shall respond to the AUX message with the same `DATA` value as incoming event for all events listed above, unless it's specified otherwise.
|
||||
|
||||
|
||||
|
||||
# Command execution flow
|
||||
|
||||
### Without interrupt
|
||||
|
||||
1. Check if command is already executing by reading `CMD_BUSY` bit in **STATUS/COMMAND** register (optional).
|
||||
1. Check if command is already executing by reading `CMD_BUSY` bit in **SCR** register (optional).
|
||||
2. Write command argument values to **DATA0** and **DATA1** registers, can be skipped if command doesn't require it.
|
||||
3. Write command ID to **STATUS/COMMAND** register.
|
||||
4. Wait for `CMD_BUSY` bit in **STATUS/COMMAND** register to go low.
|
||||
5. Check if `CMD_ERROR` bit in **STATUS/COMMAND** is set:
|
||||
3. Write command ID to **SCR** register.
|
||||
4. Wait for `CMD_BUSY` bit in **SCR** register to go low.
|
||||
5. Check if `CMD_ERROR` bit in **SCR** is set:
|
||||
- If error is set then read **DATA0** register containing error code.
|
||||
- If error is not set then read **DATA0** and **DATA1** registers containing command result values, can be skipped if command doesn't return any values.
|
||||
|
||||
### With interrupt
|
||||
|
||||
1. Check if command is already executing by reading `CMD_BUSY` bit in **STATUS/COMMAND** register (optional).
|
||||
1. Check if command is already executing by reading `CMD_BUSY` bit in **SCR** register (optional).
|
||||
2. Write command argument values to **DATA0** and **DATA1** registers, can be skipped if command doesn't require it.
|
||||
3. Write command ID to **STATUS/COMMAND** register and set `CMD_IRQ_REQUEST` bit high.
|
||||
3. Write command ID to **SCR** register and set `CMD_IRQ_REQUEST` bit high.
|
||||
4. Wait for cart interrupt.
|
||||
5. Check (in cart interrupt handler) if `CMD_IRQ_PENDING` bit in **STATUS/COMMAND** register is set high.
|
||||
5. Check (in cart interrupt handler) if `CMD_IRQ_PENDING` bit in **SCR** register is set high.
|
||||
6. Clear interrupt by setting `CMD_CLEAR` bit high in the **IRQ** register.
|
||||
7. Check if `CMD_ERROR` bit in **STATUS/COMMAND** is set:
|
||||
7. Check if `CMD_ERROR` bit in **SCR** is set:
|
||||
- If error is set then read **DATA0** register containing error code.
|
||||
- If error is not set then read **DATA0** and **DATA1** registers containing command result values, can be skipped if command doesn't return any values.
|
||||
|
@ -34,18 +34,25 @@ module n64_cfg (
|
||||
logic usb_irq;
|
||||
logic aux_irq;
|
||||
|
||||
logic usb_irq_enabled;
|
||||
logic aux_irq_enabled;
|
||||
logic btn_irq_mask;
|
||||
logic cmd_irq_mask;
|
||||
logic usb_irq_mask;
|
||||
logic aux_irq_mask;
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
irq <= (
|
||||
cmd_irq ||
|
||||
btn_irq ||
|
||||
(usb_irq_enabled && usb_irq) ||
|
||||
(aux_irq_enabled && aux_irq)
|
||||
(btn_irq && btn_irq_mask) ||
|
||||
(cmd_irq && cmd_irq_mask) ||
|
||||
(usb_irq && usb_irq_mask) ||
|
||||
(aux_irq && aux_irq_mask)
|
||||
);
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
btn_irq_mask = 1'b1;
|
||||
cmd_irq_mask = 1'b1;
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
reg_bus.rdata = 16'd0;
|
||||
if (reg_bus.address[16] && (reg_bus.address[15:5] == 11'd0)) begin
|
||||
@ -54,10 +61,14 @@ module n64_cfg (
|
||||
n64_scb.cfg_pending,
|
||||
cmd_error,
|
||||
btn_irq,
|
||||
btn_irq_mask,
|
||||
cmd_irq,
|
||||
cmd_irq_mask,
|
||||
usb_irq,
|
||||
usb_irq_mask,
|
||||
aux_irq,
|
||||
10'd0
|
||||
aux_irq_mask,
|
||||
6'd0
|
||||
};
|
||||
REG_COMMAND: reg_bus.rdata = {7'd0, cmd_irq_request, n64_scb.cfg_cmd};
|
||||
REG_DATA_0_H: reg_bus.rdata = n64_scb.cfg_wdata[0][31:16];
|
||||
@ -68,14 +79,7 @@ module n64_cfg (
|
||||
REG_IDENTIFIER_L: reg_bus.rdata = n64_scb.cfg_identifier[15:0];
|
||||
REG_KEY_H: reg_bus.rdata = 16'd0;
|
||||
REG_KEY_L: reg_bus.rdata = 16'd0;
|
||||
REG_IRQ_H: reg_bus.rdata = {
|
||||
8'd0,
|
||||
1'b1,
|
||||
1'b1,
|
||||
usb_irq_enabled,
|
||||
aux_irq_enabled,
|
||||
4'd0
|
||||
};
|
||||
REG_IRQ_H: reg_bus.rdata = 16'd0;
|
||||
REG_IRQ_L: reg_bus.rdata = 16'd0;
|
||||
REG_AUX_H: reg_bus.rdata = n64_scb.aux_wdata[31:16];
|
||||
REG_AUX_L: reg_bus.rdata = n64_scb.aux_wdata[15:0];
|
||||
@ -119,22 +123,22 @@ module n64_cfg (
|
||||
n64_scb.cfg_cmd <= 8'h00;
|
||||
cmd_error <= 1'b0;
|
||||
cmd_irq_request <= 1'b0;
|
||||
cmd_irq <= 1'b0;
|
||||
btn_irq <= 1'b0;
|
||||
cmd_irq <= 1'b0;
|
||||
usb_irq <= 1'b0;
|
||||
aux_irq <= 1'b0;
|
||||
usb_irq_enabled <= 1'b0;
|
||||
aux_irq_enabled <= 1'b0;
|
||||
usb_irq_mask <= 1'b0;
|
||||
aux_irq_mask <= 1'b0;
|
||||
lock_sequence_counter <= 1'd0;
|
||||
end else if (n64_scb.n64_reset || n64_scb.n64_nmi) begin
|
||||
n64_scb.cfg_unlock <= 1'b0;
|
||||
cmd_irq_request <= 1'b0;
|
||||
cmd_irq <= 1'b0;
|
||||
btn_irq <= 1'b0;
|
||||
cmd_irq <= 1'b0;
|
||||
usb_irq <= 1'b0;
|
||||
aux_irq <= 1'b0;
|
||||
usb_irq_enabled <= 1'b0;
|
||||
aux_irq_enabled <= 1'b0;
|
||||
usb_irq_mask <= 1'b0;
|
||||
aux_irq_mask <= 1'b0;
|
||||
lock_sequence_counter <= 1'd0;
|
||||
end else if (n64_scb.cfg_unlock) begin
|
||||
if (reg_bus.write && reg_bus.address[16] && (reg_bus.address[15:5] == 11'd0)) begin
|
||||
@ -185,12 +189,12 @@ module n64_cfg (
|
||||
if (reg_bus.wdata == 16'hFFFF) begin
|
||||
n64_scb.cfg_unlock <= 1'b0;
|
||||
cmd_irq_request <= 1'b0;
|
||||
cmd_irq <= 1'b0;
|
||||
btn_irq <= 1'b0;
|
||||
cmd_irq <= 1'b0;
|
||||
usb_irq <= 1'b0;
|
||||
aux_irq <= 1'b0;
|
||||
usb_irq_enabled <= 1'b0;
|
||||
aux_irq_enabled <= 1'b0;
|
||||
usb_irq_mask <= 1'b0;
|
||||
aux_irq_mask <= 1'b0;
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -203,17 +207,18 @@ module n64_cfg (
|
||||
end
|
||||
|
||||
REG_IRQ_L: begin
|
||||
if (reg_bus.wdata[11]) begin
|
||||
usb_irq_enabled <= 1'b0;
|
||||
end
|
||||
if (reg_bus.wdata[10]) begin
|
||||
usb_irq_enabled <= 1'b1;
|
||||
usb_irq_mask <= 1'b1;
|
||||
end
|
||||
if (reg_bus.wdata[11]) begin
|
||||
usb_irq_mask <= 1'b0;
|
||||
end
|
||||
|
||||
if (reg_bus.wdata[8]) begin
|
||||
aux_irq_mask <= 1'b1;
|
||||
end
|
||||
if (reg_bus.wdata[9]) begin
|
||||
aux_irq_enabled <= 1'b0;
|
||||
end
|
||||
if (reg_bus.wdata[8]) begin
|
||||
aux_irq_enabled <= 1'b1;
|
||||
aux_irq_mask <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
|
||||
typedef struct {
|
||||
io32_t SR_CMD;
|
||||
io32_t SCR;
|
||||
io32_t DATA[2];
|
||||
io32_t IDENTIFIER;
|
||||
io32_t KEY;
|
||||
@ -16,13 +16,17 @@ typedef struct {
|
||||
#define SC64_REGS ((sc64_regs_t *) SC64_REGS_BASE)
|
||||
|
||||
|
||||
#define SC64_SR_CMD_IRQ_REQUEST (1 << 8)
|
||||
#define SC64_SR_AUX_IRQ_PENDING (1 << 26)
|
||||
#define SC64_SR_USB_IRQ_PENDING (1 << 27)
|
||||
#define SC64_SR_CMD_IRQ_PENDING (1 << 28)
|
||||
#define SC64_SR_BTN_IRQ_PENDING (1 << 29)
|
||||
#define SC64_SR_CMD_ERROR (1 << 30)
|
||||
#define SC64_SR_CPU_BUSY (1 << 31)
|
||||
#define SC64_SCR_CPU_BUSY (1 << 31)
|
||||
#define SC64_SCR_CMD_ERROR (1 << 30)
|
||||
#define SC64_SCR_BTN_IRQ_PENDING (1 << 29)
|
||||
#define SC64_SCR_BTN_IRQ_MASK (1 << 28)
|
||||
#define SC64_SCR_CMD_IRQ_PENDING (1 << 27)
|
||||
#define SC64_SCR_CMD_IRQ_MASK (1 << 26)
|
||||
#define SC64_SCR_USB_IRQ_PENDING (1 << 25)
|
||||
#define SC64_SCR_USB_IRQ_MASK (1 << 24)
|
||||
#define SC64_SCR_AUX_IRQ_PENDING (1 << 23)
|
||||
#define SC64_SCR_AUX_IRQ_MASK (1 << 22)
|
||||
#define SC64_SCR_CMD_IRQ_REQUEST (1 << 8)
|
||||
|
||||
#define SC64_V2_IDENTIFIER (0x53437632)
|
||||
|
||||
@ -31,20 +35,14 @@ typedef struct {
|
||||
#define SC64_KEY_UNLOCK_2 (0x4F434B5FUL)
|
||||
#define SC64_KEY_LOCK (0xFFFFFFFFUL)
|
||||
|
||||
#define SC64_IRQ_AUX_ENABLE (1 << 8)
|
||||
#define SC64_IRQ_AUX_DISABLE (1 << 9)
|
||||
#define SC64_IRQ_USB_ENABLE (1 << 10)
|
||||
#define SC64_IRQ_USB_DISABLE (1 << 11)
|
||||
|
||||
#define SC64_IRQ_AUX_MASK (1 << 20)
|
||||
#define SC64_IRQ_USB_MASK (1 << 21)
|
||||
#define SC64_IRQ_CMD_MASK (1 << 22)
|
||||
#define SC64_IRQ_BTN_MASK (1 << 23)
|
||||
|
||||
#define SC64_IRQ_AUX_CLEAR (1 << 28)
|
||||
#define SC64_IRQ_USB_CLEAR (1 << 29)
|
||||
#define SC64_IRQ_CMD_CLEAR (1 << 30)
|
||||
#define SC64_IRQ_BTN_CLEAR (1 << 31)
|
||||
#define SC64_IRQ_CMD_CLEAR (1 << 30)
|
||||
#define SC64_IRQ_USB_CLEAR (1 << 29)
|
||||
#define SC64_IRQ_AUX_CLEAR (1 << 28)
|
||||
#define SC64_IRQ_USB_DISABLE (1 << 11)
|
||||
#define SC64_IRQ_USB_ENABLE (1 << 10)
|
||||
#define SC64_IRQ_AUX_DISABLE (1 << 9)
|
||||
#define SC64_IRQ_AUX_ENABLE (1 << 8)
|
||||
|
||||
|
||||
typedef enum {
|
||||
@ -101,20 +99,20 @@ static sc64_error_t sc64_execute_cmd (sc64_cmd_t *cmd) {
|
||||
|
||||
if (use_cmd_irq) {
|
||||
wait_cmd_irq = true;
|
||||
pi_io_write(&SC64_REGS->SR_CMD, (SC64_SR_CMD_IRQ_REQUEST | (cmd->id & 0xFF)));
|
||||
pi_io_write(&SC64_REGS->SCR, (SC64_SCR_CMD_IRQ_REQUEST | (cmd->id & 0xFF)));
|
||||
while (wait_cmd_irq);
|
||||
sr = pi_io_read(&SC64_REGS->SR_CMD);
|
||||
if (sr & SC64_SR_CPU_BUSY) {
|
||||
sr = pi_io_read(&SC64_REGS->SCR);
|
||||
if (sr & SC64_SCR_CPU_BUSY) {
|
||||
error_display("[Unexpected] SC64 CMD busy flag set");
|
||||
}
|
||||
} else {
|
||||
pi_io_write(&SC64_REGS->SR_CMD, (cmd->id & 0xFF));
|
||||
pi_io_write(&SC64_REGS->SCR, (cmd->id & 0xFF));
|
||||
do {
|
||||
sr = pi_io_read(&SC64_REGS->SR_CMD);
|
||||
} while (sr & SC64_SR_CPU_BUSY);
|
||||
sr = pi_io_read(&SC64_REGS->SCR);
|
||||
} while (sr & SC64_SCR_CPU_BUSY);
|
||||
}
|
||||
|
||||
if (sr & SC64_SR_CMD_ERROR) {
|
||||
if (sr & SC64_SCR_CMD_ERROR) {
|
||||
return (sc64_error_t) (pi_io_read(&SC64_REGS->DATA[0]));
|
||||
}
|
||||
|
||||
@ -218,7 +216,7 @@ void sc64_lock (void) {
|
||||
bool sc64_check_presence (void) {
|
||||
bool detected = (pi_io_read(&SC64_REGS->IDENTIFIER) == SC64_V2_IDENTIFIER);
|
||||
if (detected) {
|
||||
while (pi_io_read(&SC64_REGS->SR_CMD) & SC64_SR_CPU_BUSY);
|
||||
while (pi_io_read(&SC64_REGS->SCR) & SC64_SCR_CPU_BUSY);
|
||||
}
|
||||
return detected;
|
||||
}
|
||||
@ -237,18 +235,18 @@ void sc64_aux_irq_enable (bool enable) {
|
||||
}
|
||||
|
||||
sc64_irq_t sc64_irq_pending (void) {
|
||||
uint32_t sr = pi_io_read(&SC64_REGS->SR_CMD);
|
||||
uint32_t sr = pi_io_read(&SC64_REGS->SCR);
|
||||
sc64_irq_t irq = SC64_IRQ_NONE;
|
||||
if (sr & SC64_SR_BTN_IRQ_PENDING) {
|
||||
if ((sr & SC64_SCR_BTN_IRQ_MASK) && (sr & SC64_SCR_BTN_IRQ_PENDING)) {
|
||||
irq |= SC64_IRQ_BTN;
|
||||
}
|
||||
if (sr & SC64_SR_CMD_IRQ_PENDING) {
|
||||
if ((sr & SC64_SCR_CMD_IRQ_MASK) && (sr & SC64_SCR_CMD_IRQ_PENDING)) {
|
||||
irq |= SC64_IRQ_CMD;
|
||||
}
|
||||
if (sr & SC64_SR_USB_IRQ_PENDING) {
|
||||
if ((sr & SC64_SCR_USB_IRQ_MASK) && (sr & SC64_SCR_USB_IRQ_PENDING)) {
|
||||
irq |= SC64_IRQ_USB;
|
||||
}
|
||||
if (sr & SC64_SR_AUX_IRQ_PENDING) {
|
||||
if ((sr & SC64_SCR_AUX_IRQ_MASK) && (sr & SC64_SCR_AUX_IRQ_PENDING)) {
|
||||
irq |= SC64_IRQ_AUX;
|
||||
}
|
||||
return irq;
|
||||
|
@ -140,6 +140,10 @@ struct _64DDArgs {
|
||||
#[arg(short, long)]
|
||||
rom: Option<PathBuf>,
|
||||
|
||||
/// Attempt to reboot the console (requires specific support in the running game)
|
||||
#[arg(short, long)]
|
||||
reboot: bool,
|
||||
|
||||
/// Path to the save file (also used by save writeback mechanism)
|
||||
#[arg(short, long, requires = "rom")]
|
||||
save: Option<PathBuf>,
|
||||
@ -367,27 +371,13 @@ fn handle_list_command() -> Result<(), sc64::Error> {
|
||||
}
|
||||
|
||||
fn handle_upload_command(connection: Connection, args: &UploadArgs) -> Result<(), sc64::Error> {
|
||||
const AUX_TOKEN_UPLOAD_START: u32 = 0xFF000001;
|
||||
const AUX_TOKEN_REBOOT: u32 = 0xFF000002;
|
||||
|
||||
let mut sc64 = init_sc64(connection, true)?;
|
||||
|
||||
if args.reboot {
|
||||
match sc64.aux_send_and_receive(
|
||||
AUX_TOKEN_UPLOAD_START,
|
||||
std::time::Duration::from_millis(500),
|
||||
)? {
|
||||
Some(data) => println!(
|
||||
if args.reboot && !sc64.aux_try_notify(sc64::AuxMessage::IOHalt)? {
|
||||
println!(
|
||||
"{}",
|
||||
format!("N64 reboot prepare successful (0x{data:08X})")
|
||||
.bright_green()
|
||||
.bold()
|
||||
),
|
||||
None => println!(
|
||||
"{}",
|
||||
"N64 reboot prepare unsuccessful".bright_yellow().bold()
|
||||
),
|
||||
}
|
||||
"Warning: no response for [IOHalt] AUX message".bright_yellow()
|
||||
);
|
||||
}
|
||||
|
||||
sc64.reset_state()?;
|
||||
@ -435,8 +425,11 @@ fn handle_upload_command(connection: Connection, args: &UploadArgs) -> Result<()
|
||||
|
||||
sc64.calculate_cic_parameters(args.cic_seed)?;
|
||||
|
||||
if args.reboot {
|
||||
sc64.aux_send(AUX_TOKEN_REBOOT)?;
|
||||
if args.reboot && !sc64.aux_try_notify(sc64::AuxMessage::Reboot)? {
|
||||
println!(
|
||||
"{}",
|
||||
"Warning: no response for [Reboot] AUX message".bright_yellow()
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -477,6 +470,13 @@ fn handle_64dd_command(connection: Connection, args: &_64DDArgs) -> Result<(), s
|
||||
.bright_green()
|
||||
);
|
||||
|
||||
if args.reboot && !sc64.aux_try_notify(sc64::AuxMessage::IOHalt)? {
|
||||
println!(
|
||||
"{}",
|
||||
"Warning: no response for [IOHalt] AUX message".bright_yellow()
|
||||
);
|
||||
}
|
||||
|
||||
sc64.reset_state()?;
|
||||
|
||||
if let Some(rom) = &args.rom {
|
||||
@ -587,6 +587,13 @@ fn handle_64dd_command(connection: Connection, args: &_64DDArgs) -> Result<(), s
|
||||
|
||||
sc64.set_save_writeback(true)?;
|
||||
|
||||
if args.reboot && !sc64.aux_try_notify(sc64::AuxMessage::Reboot)? {
|
||||
println!(
|
||||
"{}",
|
||||
"Warning: no response for [Reboot] AUX message".bright_yellow()
|
||||
);
|
||||
}
|
||||
|
||||
let exit = setup_exit_flag();
|
||||
while !exit.load(Ordering::Relaxed) {
|
||||
if let Some(data_packet) = sc64.receive_data_packet()? {
|
||||
|
@ -13,10 +13,10 @@ pub use self::{
|
||||
link::list_local_devices,
|
||||
server::ServerEvent,
|
||||
types::{
|
||||
BootMode, ButtonMode, ButtonState, CicSeed, DataPacket, DdDiskState, DdDriveType, DdMode,
|
||||
DebugPacket, DiagnosticData, DiskPacket, DiskPacketKind, FpgaDebugData, ISViewer,
|
||||
MemoryTestPattern, MemoryTestPatternResult, SaveType, SaveWriteback, SpeedTestDirection,
|
||||
Switch, TvType,
|
||||
AuxMessage, BootMode, ButtonMode, ButtonState, CicSeed, DataPacket, DdDiskState,
|
||||
DdDriveType, DdMode, DebugPacket, DiagnosticData, DiskPacket, DiskPacketKind,
|
||||
FpgaDebugData, ISViewer, MemoryTestPattern, MemoryTestPatternResult, SaveType,
|
||||
SaveWriteback, SpeedTestDirection, Switch, TvType,
|
||||
},
|
||||
};
|
||||
|
||||
@ -598,6 +598,15 @@ impl SC64 {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn aux_try_notify(&mut self, message: AuxMessage) -> Result<bool, Error> {
|
||||
let value: u32 = message.into();
|
||||
let timeout = std::time::Duration::from_millis(500);
|
||||
if let Some(response) = self.aux_send_and_receive(value, timeout)? {
|
||||
return Ok(value == response);
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
pub fn check_device(&mut self) -> Result<(), Error> {
|
||||
let identifier = self.command_identifier_get().map_err(|e| {
|
||||
Error::new(format!("Couldn't get SC64 device identifier: {e}").as_str())
|
||||
|
@ -650,6 +650,20 @@ impl TryFrom<AsynchronousPacket> for DataPacket {
|
||||
}
|
||||
}
|
||||
|
||||
pub enum AuxMessage {
|
||||
IOHalt,
|
||||
Reboot,
|
||||
}
|
||||
|
||||
impl From<AuxMessage> for u32 {
|
||||
fn from(value: AuxMessage) -> Self {
|
||||
match value {
|
||||
AuxMessage::IOHalt => 0xFF000001,
|
||||
AuxMessage::Reboot => 0xFF000002,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DebugPacket {
|
||||
pub datatype: u8,
|
||||
pub data: Vec<u8>,
|
||||
|
Loading…
x
Reference in New Issue
Block a user