mirror of
https://github.com/Polprzewodnikowy/SummerCart64.git
synced 2025-04-07 11:56:36 +02:00
Compare commits
32 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
88d654129a | ||
![]() |
b88c9a314b | ||
![]() |
e4c3f34fb0 | ||
![]() |
b520f9ace8 | ||
![]() |
d8c4f979cc | ||
![]() |
d63f5893da | ||
![]() |
d307e1a5b1 | ||
![]() |
65f8fa3cf7 | ||
![]() |
6c566bd530 | ||
![]() |
99060bec15 | ||
![]() |
63feaa0c2e | ||
![]() |
a59ad1d39b | ||
![]() |
30fb3d0ea6 | ||
![]() |
a3d4082384 | ||
![]() |
0739ca624c | ||
![]() |
bb1ce45dfe | ||
![]() |
9193e9c6f2 | ||
![]() |
3fbb6f3823 | ||
![]() |
f546e5d17d | ||
![]() |
8393963650 | ||
![]() |
20a9ec0087 | ||
![]() |
b3d9e98e68 | ||
![]() |
6698550dbd | ||
![]() |
18041e2547 | ||
![]() |
0538a28f9e | ||
![]() |
1ade3ade8e | ||
![]() |
80b4aa95cd | ||
![]() |
6eef811cd6 | ||
![]() |
e2c100ae7f | ||
![]() |
a6e86587ae | ||
![]() |
93ab101be4 | ||
![]() |
cc41652e6f |
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
@ -1,2 +1 @@
|
||||
github: polprzewodnikowy
|
||||
ko_fi: polprzewodnikowy
|
||||
|
27
.github/ISSUE_TEMPLATE/bug_report.md
vendored
27
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,27 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
4.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Possible solution**
|
||||
Not obligatory, but suggest a fix/reason for the bug.
|
58
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
58
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
name: Bug Report
|
||||
description: File a bug report
|
||||
title: "[SC64][BUG] "
|
||||
labels: ["bug"]
|
||||
assignees:
|
||||
- Polprzewodnikowy
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report!
|
||||
- type: checkboxes
|
||||
id: sanity-check
|
||||
attributes:
|
||||
label: Is your issue really a bug?
|
||||
description: |
|
||||
Issue tracker in this repository is for **BUG REPORTS ONLY**.
|
||||
|
||||
Make sure your problem is caused by the firmware/PC app and **not** by the software you're running on the flashcart.
|
||||
|
||||
Errors in the documentation are also considered a bug.
|
||||
|
||||
If your issue is related to the menu then report it in the [N64FlashcartMenu] repository.
|
||||
|
||||
[N64FlashcartMenu]: https://github.com/Polprzewodnikowy/N64FlashcartMenu
|
||||
options:
|
||||
- label: I understand the difference between flashcart firmware, N64FlashcartMenu and `sc64deployer` PC app.
|
||||
required: true
|
||||
- label: I found a bug in FPGA HDL (`/fw/rtl`)
|
||||
- label: I found a bug in MCU app (`/sw/controller`)
|
||||
- label: I found a bug in N64 bootloader (`/sw/bootloader`)
|
||||
- label: I found a bug in PC app (`/sw/deployer`)
|
||||
- label: I found a bug in initial programming script (`/sw/tools/primer.py`)
|
||||
- label: I found an error in documentation (`/docs`)
|
||||
- label: I found an issue elsewhere
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Firmware version
|
||||
placeholder: v2.20.2
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: Describe the bug
|
||||
description: |
|
||||
Tell us what you noticed as a bug, and what is your expected outcome.
|
||||
The more detailed the description is the better.
|
||||
If applicable please attach screenshots and/or video showing the problem.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: deployer-info
|
||||
attributes:
|
||||
label: Output logs from `sc64deployer info`
|
||||
description: If possible, please copy and paste the output from the command specified above.
|
||||
render: shell
|
5
.github/ISSUE_TEMPLATE/config.yml
vendored
5
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,8 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: GitHub Discussions
|
||||
url: https://github.com/Polprzewodnikowy/SummerCart64/discussions
|
||||
about: Please use for QUESTIONS, conversations or discussions.
|
||||
- name: N64brew Discord
|
||||
url: https://discord.gg/8VNMKhxqQn
|
||||
about: Alternative channel for asking QUESTIONS.
|
||||
about: The go-to community to ask about SummerCart64
|
||||
|
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -1,20 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
20
.github/PULL_REQUEST_TEMPLATE/default.md
vendored
20
.github/PULL_REQUEST_TEMPLATE/default.md
vendored
@ -1,20 +0,0 @@
|
||||
**Description**
|
||||
Provide a headline summary of your changes in the Title above.
|
||||
Describe your changes in detail.
|
||||
|
||||
**Related Issue**
|
||||
This project only accepts pull requests related to open issues.
|
||||
If suggesting a change, please discuss it in an issue first.
|
||||
If fixing a bug, there should be an issue describing it with steps to reproduce.
|
||||
Please link to the issue here:
|
||||
|
||||
**Motivation and Context**
|
||||
Why is this change required? What problem does it solve?
|
||||
If it fixes an open issue, please link to the issue here.
|
||||
|
||||
**How Has This Been Tested?**
|
||||
Please describe in detail how you tested your changes.
|
||||
Include details of your testing environment, and the tests you ran to see how your change affects other areas of the code, etc.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help demonstrate your feature/bugfix.
|
8
.github/workflows/build.yml
vendored
8
.github/workflows/build.yml
vendored
@ -19,6 +19,8 @@ jobs:
|
||||
steps:
|
||||
- name: Download SummerCart64 repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set package version
|
||||
uses: frabert/replace-string-action@v2
|
||||
@ -28,9 +30,15 @@ jobs:
|
||||
string: '${{ github.ref_name }}'
|
||||
replace-with: '-'
|
||||
|
||||
- name: Retrieve the Lattice Diamond license from secrets and decode it
|
||||
run: echo $LICENSE | base64 --decode > fw/project/lcmxo2/license.dat
|
||||
env:
|
||||
LICENSE: ${{ secrets.LATTICE_DIAMOND_LICENSE_BASE64 }}
|
||||
|
||||
- name: Build firmware
|
||||
run: ./docker_build.sh release --force-clean
|
||||
env:
|
||||
MAC_ADDRESS: ${{ secrets.LATTICE_DIAMOND_MAC_ADDRESS }}
|
||||
SC64_VERSION: ${{ steps.version.outputs.replaced }}
|
||||
|
||||
- name: Upload artifact
|
||||
|
66
README.md
66
README.md
@ -4,25 +4,40 @@
|
||||
**For non-technical description of the SummerCart64, please head to the https://summercart64.dev website!**
|
||||
|
||||
## Features
|
||||
- 64 MiB SDRAM memory for game and save data
|
||||
- 16 MiB FLASH memory for bootloader and extended game data
|
||||
- 8 kiB on-chip buffer for general use
|
||||
- ~23.8 MiB/s peak transfer rate USB interface for data upload/download and debug functionality
|
||||
- ~23.8 MiB/s peak transfer rate SD card interface
|
||||
- EEPROM, SRAM and FlashRAM save types with automatic writeback to SD card
|
||||
- Battery backed real time clock (RTC)
|
||||
- Status LED and button for general use
|
||||
- 64DD add-on emulation
|
||||
- IS-Viewer 64 debug interface
|
||||
- N64 bootloader with support for IPL3 registers spoofing and loading menu from SD card
|
||||
- Dedicated open source menu written specifically for this flashcart - [N64FlashcartMenu](https://github.com/Polprzewodnikowy/N64FlashcartMenu)
|
||||
- Enhanced [UltraCIC_C](https://github.com/jago85/UltraCIC_C) emulation with automatic region switching and programmable seed/checksum values
|
||||
- PC app for communicating with flashcart (game/save data upload/download, feature enable control and debug terminal)
|
||||
- Access to the SD card via USB interface with the use of the PC app
|
||||
- [UNFLoader](https://github.com/buu342/N64-UNFLoader) support
|
||||
- Initial programming via UART header or dedicated JTAG/SWD interfaces
|
||||
- Software and firmware updatable via USB interface
|
||||
- 3D printable plastic shell
|
||||
|
||||
- **ROM and Save Memory On-board**
|
||||
- 64 MiB SDRAM memory for game and save data (enough memory to support every retail game without compromise)
|
||||
- 16 MiB FLASH memory for bootloader and extended game data (with extended memory flashcart supports game ROMs up to 78 MiB)
|
||||
|
||||
- **Game Saves**
|
||||
- EEPROM 4k/16k, SRAM and FlashRAM save types with an automatic writeback to the SD card (no reset button press required)
|
||||
|
||||
- **Hardware-Dependent Game Features**
|
||||
- 64DD add-on emulation
|
||||
- Battery backed real time clock (RTC)
|
||||
|
||||
- **Menu**
|
||||
- Dedicated open source menu written specifically for this flashcart - [N64FlashcartMenu](https://github.com/Polprzewodnikowy/N64FlashcartMenu)
|
||||
|
||||
- **Game Development**
|
||||
- ~23.8 MiB/s peak transfer rate SD card interface
|
||||
- ~23.8 MiB/s peak transfer rate USB interface for data upload/download and debug functionality
|
||||
- PC app to access the flashcart features:
|
||||
- Game/save data upload/download
|
||||
- Feature enable control
|
||||
- Debug terminal
|
||||
- Access to the SD card
|
||||
- Firmware update
|
||||
- [UNFLoader](https://github.com/buu342/N64-UNFLoader) support
|
||||
- IS-Viewer 64 debug interface (fixed 64 kiB buffer with a movable base address)
|
||||
- 8 kiB on-chip buffer for general use
|
||||
- Status LED and button for general use
|
||||
- [UltraCIC_C](https://github.com/jago85/UltraCIC_C) emulation with automatic region switching and programmable seed/checksum values
|
||||
- N64 bootloader with support for IPL3 registers spoofing and loading menu from SD card
|
||||
|
||||
- **Cartridge Production**
|
||||
- Initial programming via UART header or via dedicated JTAG/SWD interfaces
|
||||
- 3D printable shell
|
||||
|
||||
---
|
||||
|
||||
@ -38,17 +53,6 @@
|
||||
|
||||
---
|
||||
|
||||
## Help / Q&A
|
||||
|
||||
For any questions related to this project, please use [*Discussions*](https://github.com/Polprzewodnikowy/SummerCart64/discussions) tab in GitHub repository.
|
||||
Using discussions tab is highly encouraged as it allows to have centralized knowledge database accessible for everyone interested in this project.
|
||||
|
||||
I'm also active at [N64brew](https://discord.gg/8VNMKhxqQn) Discord server as `korgeaux` but keep in mind that [*Discussions*](https://github.com/Polprzewodnikowy/SummerCart64/discussions) tab is a preferred option.
|
||||
|
||||
Note that my time is limited so I can't answer all questions.
|
||||
|
||||
---
|
||||
|
||||
## How do I get one?
|
||||
|
||||
Most up to date information about purchasing/manufacturing options is available on https://summercart64.dev website!
|
||||
@ -64,7 +68,7 @@ If you have even slightest doubt about the ordering or programming process, it i
|
||||
|
||||
**Full disclosure**: for every order made through [this link](https://www.pcbway.com/project/member/shareproject/?bmbno=1046ED64-8AEE-44) I will receive 10% of PCB manufacturing and PCB assembly service cost (price of the components is not included in the split). This is a great way of supporting further project development.
|
||||
|
||||
If you don't need a physical product but still want to support me then check my [GitHub sponsors](https://github.com/sponsors/Polprzewodnikowy) page.
|
||||
If you don't need a physical product but still want to support me then check the sponsor links on the [official website](https://summercart64.dev).
|
||||
|
||||
---
|
||||
|
||||
|
30
build.sh
30
build.sh
@ -17,6 +17,7 @@ TOP_FILES=(
|
||||
FILES=(
|
||||
"./assets/*"
|
||||
"./docs/*"
|
||||
"./hw/pcb/LICENSE"
|
||||
"./hw/pcb/sc64v2_bom.html"
|
||||
"./hw/pcb/sc64v2.kicad_pcb"
|
||||
"./hw/pcb/sc64v2.kicad_pro"
|
||||
@ -27,6 +28,8 @@ FILES=(
|
||||
"./README.md"
|
||||
)
|
||||
|
||||
HAVE_COMMIT_INFO=false
|
||||
|
||||
BUILT_BOOTLOADER=false
|
||||
BUILT_CONTROLLER=false
|
||||
BUILT_CIC=false
|
||||
@ -36,9 +39,24 @@ BUILT_RELEASE=false
|
||||
|
||||
FORCE_CLEAN=false
|
||||
|
||||
get_last_commit_info () {
|
||||
if [ "$HAVE_COMMIT_INFO" = true ]; then return; fi
|
||||
|
||||
SAFE_DIRECTORY="-c safe.directory=$(pwd)"
|
||||
|
||||
GIT_BRANCH=$(git $SAFE_DIRECTORY rev-parse --abbrev-ref HEAD)
|
||||
GIT_TAG=$(git $SAFE_DIRECTORY describe --tags 2> /dev/null)
|
||||
GIT_SHA=$(git $SAFE_DIRECTORY rev-parse HEAD)
|
||||
GIT_MESSAGE=$(git $SAFE_DIRECTORY log --oneline --format=%B -n 1 HEAD | head -n 1)
|
||||
|
||||
HAVE_COMMIT_INFO=true
|
||||
}
|
||||
|
||||
build_bootloader () {
|
||||
if [ "$BUILT_BOOTLOADER" = true ]; then return; fi
|
||||
|
||||
get_last_commit_info
|
||||
|
||||
pushd sw/bootloader > /dev/null
|
||||
if [ "$FORCE_CLEAN" = true ]; then
|
||||
make clean
|
||||
@ -98,6 +116,8 @@ build_fpga () {
|
||||
build_update () {
|
||||
if [ "$BUILT_UPDATE" = true ]; then return; fi
|
||||
|
||||
get_last_commit_info
|
||||
|
||||
build_bootloader
|
||||
build_controller
|
||||
build_cic
|
||||
@ -163,6 +183,14 @@ if test $# -eq 0; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_time () {
|
||||
echo "Build took $SECONDS seconds"
|
||||
}
|
||||
|
||||
trap "echo \"Build failed\"; print_time" ERR
|
||||
|
||||
SECONDS=0
|
||||
|
||||
TRIGGER_BOOTLOADER=false
|
||||
TRIGGER_CONTROLLER=false
|
||||
TRIGGER_CIC=false
|
||||
@ -213,3 +241,5 @@ if [ "$TRIGGER_CIC" = true ]; then build_cic; fi
|
||||
if [ "$TRIGGER_FPGA" = true ]; then build_fpga; fi
|
||||
if [ "$TRIGGER_UPDATE" = true ]; then build_update; fi
|
||||
if [ "$TRIGGER_RELEASE" = true ]; then build_release; fi
|
||||
|
||||
print_time
|
||||
|
@ -1,20 +1,14 @@
|
||||
#!/bin/bash
|
||||
|
||||
BUILDER_IMAGE="ghcr.io/polprzewodnikowy/sc64env:v1.10"
|
||||
BUILDER_PLATFORM="linux/x86_64"
|
||||
|
||||
pushd $(dirname $0) > /dev/null
|
||||
|
||||
GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||
GIT_TAG=$(git describe --tags 2> /dev/null)
|
||||
GIT_SHA=$(git rev-parse HEAD)
|
||||
GIT_MESSAGE=$(git log --oneline --format=%B -n 1 HEAD | head -n 1)
|
||||
|
||||
if [ -t 1 ]; then
|
||||
DOCKER_OPTIONS="-it"
|
||||
fi
|
||||
|
||||
SECONDS=0
|
||||
|
||||
docker run \
|
||||
$DOCKER_OPTIONS \
|
||||
--rm \
|
||||
@ -23,20 +17,13 @@ docker run \
|
||||
-v "$(pwd)"/fw/project/lcmxo2/license.dat:/flexlm/license.dat \
|
||||
-v "$(pwd)":/workdir \
|
||||
-h=`hostname` \
|
||||
-e GIT_BRANCH="$GIT_BRANCH" \
|
||||
-e GIT_TAG="$GIT_TAG" \
|
||||
-e GIT_SHA="$GIT_SHA" \
|
||||
-e GIT_MESSAGE="$GIT_MESSAGE" \
|
||||
-e SC64_VERSION=${SC64_VERSION:-""} \
|
||||
--platform $BUILDER_PLATFORM \
|
||||
$BUILDER_IMAGE \
|
||||
./build.sh $@
|
||||
|
||||
BUILD_ERROR=$?
|
||||
|
||||
echo "Build took $SECONDS seconds"
|
||||
BUILD_RESULT=$?
|
||||
|
||||
popd > /dev/null
|
||||
|
||||
if [ $BUILD_ERROR -ne 0 ]; then
|
||||
exit -1
|
||||
fi
|
||||
exit $BUILD_RESULT
|
||||
|
@ -3,7 +3,7 @@
|
||||
- [PC -\> SC64 packets](#pc---sc64-packets)
|
||||
- [**`CMD`** packet](#cmd-packet)
|
||||
- [SC64 -\> PC packets](#sc64---pc-packets)
|
||||
- [**`RSP`/`ERR`** packets](#rsperr-packets)
|
||||
- [**`CMP`/`ERR`** packets](#cmperr-packets)
|
||||
- [**`PKT`** packet](#pkt-packet)
|
||||
- [Supported commands](#supported-commands)
|
||||
- [`v`: **IDENTIFIER\_GET**](#v-identifier_get)
|
||||
@ -128,29 +128,29 @@ Packet data length is derived from the argument if specific command supports it.
|
||||
|
||||
| identifier | description |
|
||||
| ---------- | ---------------------------------------- |
|
||||
| `RSP` | Success response to the received command |
|
||||
| `CMP` | Success response to the received command |
|
||||
| `ERR` | Error response to the received command |
|
||||
| `PKT` | Asynchronous data packet |
|
||||
|
||||
SC64 sends response packet `RSP`/`ERR` to almost every command received from the PC.
|
||||
SC64 sends response packet `CMP`/`ERR` to almost every command received from the PC.
|
||||
Fourth byte is the same as in the command that triggered the response.
|
||||
If command execution was not successful, then `RSP` identifier is replaced by the `ERR` identifier.
|
||||
If command execution was not successful, then `CMP` identifier is replaced by the `ERR` identifier.
|
||||
|
||||
SC64 can also send `PKT` packet at any time as a response to action triggered by the N64 or the flashcart itself.
|
||||
Fourth byte denotes packet ID listed in the [asynchronous packets](#asynchronous-packets) section.
|
||||
|
||||
#### **`RSP`/`ERR`** packets
|
||||
#### **`CMP`/`ERR`** packets
|
||||
|
||||
General structure of packet:
|
||||
| offset | type | description |
|
||||
| ------ | -------------------- | ---------------------- |
|
||||
| `0` | char[3] | `RSP`/`ERR` identifier |
|
||||
| `0` | char[3] | `CMP`/`ERR` identifier |
|
||||
| `3` | char[1] | Command ID |
|
||||
| `4` | uint32_t | Response data length |
|
||||
| `8` | uint8_t[data_length] | Response data (if any) |
|
||||
|
||||
`RSP`/`ERR` packet is sent as a response to the command sent by the PC.
|
||||
`ERR` response might contain no (or undefined) data in the arbitrary data field compared to regular `RSP` packet.
|
||||
`CMP`/`ERR` packet is sent as a response to the command sent by the PC.
|
||||
`ERR` response might contain no (or undefined) data in the arbitrary data field compared to regular `CMP` packet.
|
||||
|
||||
#### **`PKT`** packet
|
||||
|
||||
@ -463,7 +463,7 @@ Writes bytes to the specified memory address. Please refer to the [internal memo
|
||||
|
||||
_This command does not send response data._
|
||||
|
||||
_This command does not send `RSP`/`ERR` packet response!_
|
||||
_This command does not send `CMP`/`ERR` packet response!_
|
||||
|
||||
This command notifies N64 that data is waiting to be acknowledged.
|
||||
If N64 side doesn't acknowledge data via [`m` **USB_READ**](./02_n64_commands.md#m-usb_read) N64 command within 1 second then data is flushed and [`G` **DATA_FLUSHED**](#asynchronous-packets) asynchronous packet is sent to the PC.
|
||||
|
@ -133,9 +133,10 @@ type: *enum* | default: `0`
|
||||
- `4` - FlashRAM 1 Mib save is enabled
|
||||
- `5` - SRAM 768 kib save is enabled
|
||||
- `6` - SRAM 1 Mib save is enabled
|
||||
- `7` - FakeFlashRAM 1 Mib save is enabled (write/erase timings are not emulated and erase before write is not required)
|
||||
|
||||
Use this setting for selecting save type that will be emulated. Only one save type can be enabled.
|
||||
Any successful write to this config will disable automatic save writeback to USB or SD card when previously enabled.
|
||||
Any successful write to this config will disable automatic save writeback to the USB or SD card if previously enabled.
|
||||
|
||||
---
|
||||
|
||||
|
@ -10,13 +10,14 @@
|
||||
|
||||
Docker method is a preferred option.
|
||||
Run `./docker_build.sh release` to build all firmware/software and generate release package.
|
||||
For other options run script without any command to print help.
|
||||
For other options run script without any command to print help about available options.
|
||||
|
||||
#### Lattice Diamond license
|
||||
|
||||
Lattice Diamond software is used to build FPGA bitstream, a free 1 year license is necessary to run the build process.
|
||||
Repository already contains license attached to fake MAC address `F8:12:34:56:78:90` (path to the license: `fw/project/lcmxo2/license.dat`).
|
||||
If the license expires, it is required to request new license from Lattice webpage.
|
||||
New license can be attached to aforementioned MAC address or any other address.
|
||||
In case of non default MAC address it is possible to provide it via `MAC_ADDRESS` environment variable.
|
||||
For example: `MAC_ADDRESS=AB:00:00:00:00:00 ./docker_build.sh release`.
|
||||
Lattice Diamond software is used to build the FPGA bitstream.
|
||||
A free 1 year license is necessary to run the build process.
|
||||
You can request personal license from the [Lattice website](https://www.latticesemi.com/Support/Licensing).
|
||||
Build script expects license file to be present in this path: `fw/project/lcmxo2/license.dat`.
|
||||
Since build is done inside docker container it is required to pass the MAC address, linked with the license file, to a container.
|
||||
Build script expects it in the `MAC_ADDRESS` environment variable.
|
||||
For example, run `MAC_ADDRESS=AB:00:00:00:00:00 ./docker_build.sh release` command to start the building process if your license is attached to `AB:00:00:00:00:00` MAC address.
|
||||
|
@ -1,3 +1,4 @@
|
||||
- [Video guide](#video-guide)
|
||||
- [Step by step guide how to make SC64](#step-by-step-guide-how-to-make-sc64)
|
||||
- [**PCB manufacturing data**](#pcb-manufacturing-data)
|
||||
- [**PCB requirements**](#pcb-requirements)
|
||||
@ -12,6 +13,10 @@
|
||||
|
||||
---
|
||||
|
||||
## Video guide
|
||||
|
||||
[](https://www.youtube.com/watch?v=t6hyCFpwqz8 "How to build and program the SummerCart64")
|
||||
|
||||
## Step by step guide how to make SC64
|
||||
|
||||
All necessary manufacturing files are packaged in every `sc64-extra-{version}.zip` file in GitHub releases.
|
||||
@ -42,7 +47,7 @@ Please download latest release before proceeding with the instructions.
|
||||
|
||||
### **Components**
|
||||
|
||||
1. Locate interactive BOM file inside `hw/pcb` folder (alternatively, check out this [BOM discussion](https://github.com/Polprzewodnikowy/SummerCart64/discussions/27))
|
||||
1. Locate the interactive BOM file inside `hw/pcb` folder
|
||||
2. Order all parts listed in the BOM file or use PCB assembly service together with your PCB order
|
||||
|
||||
---
|
||||
@ -137,5 +142,3 @@ Please use command `python3 primer.py COMx sc64-firmware-{version}.bin --bootloa
|
||||
Due to multiple possible causes of the problem it's best to start visually inspecting SC64's board for any defects, like bad solder work or chips soldered backwards.
|
||||
If visual inspection didn't yield any obvious culprits then next step would be to check if everything is connected correctly.
|
||||
Check if TX/RX signals aren't swapped and if SC64 is getting power from the USB cable. Best place to check supply voltage are the exposed test pads on the left of U8 chip.
|
||||
If everything at this point was checked and looked fine, then feel free to open new thread in the [*Discussions*](https://github.com/Polprzewodnikowy/SummerCart64/discussions) tab.
|
||||
Make sure to describe your problem extensively, attach SC64 board photos **from the both sides**, and paste all logs/screenshots from the `primer.py` output.
|
||||
|
1
fw/project/lcmxo2/.gitignore
vendored
1
fw/project/lcmxo2/.gitignore
vendored
@ -1,6 +1,7 @@
|
||||
.recovery
|
||||
*.dir/
|
||||
*.ccl
|
||||
*.dat
|
||||
*.dmp
|
||||
*.html
|
||||
*.ini
|
||||
|
@ -1,30 +0,0 @@
|
||||
FEATURE LSC_BASE lattice 8.0 23-feb-2025 uncounted EEEED0E56442 \
|
||||
VENDOR_STRING="ispLEVER BASE" HOSTID=f81234567890
|
||||
FEATURE LSC_SYNPLIFY lattice 8.0 23-feb-2025 uncounted BCE24EFB1CC8 \
|
||||
VENDOR_STRING="ispLEVER System with Synplicity" \
|
||||
HOSTID=f81234567890
|
||||
FEATURE LSC_SYNPLIFYPRO1 lattice 10.0 23-feb-2025 uncounted \
|
||||
A55E225A8213 VENDOR_STRING="ispLEVER System with Synplicity \
|
||||
Pro 1" HOSTID=f81234567890
|
||||
FEATURE LSC_ADVANCED_DSP lattice 10.0 23-feb-2025 uncounted \
|
||||
670E6D281E0E VENDOR_STRING="ispLEVER DSP" HOSTID=f81234567890
|
||||
FEATURE LSC_DIAMOND_A lattice 10.0 23-feb-2025 uncounted 75EFC1B810BA \
|
||||
VENDOR_STRING="Diamond Free" HOSTID=f81234567890
|
||||
FEATURE LSC_PROGRAMMER_MATURE lattice 10.0 23-feb-2025 uncounted \
|
||||
46BCF34C2DC7 VENDOR_STRING=Programmer HOSTID=f81234567890
|
||||
FEATURE LSC_ADVANCED_ORCA lattice 9.0 23-feb-2025 uncounted \
|
||||
CC2DBC156EE2 VENDOR_STRING="ispORCA System" \
|
||||
HOSTID=f81234567890
|
||||
FEATURE LSC_CTL_PROPBLD lattice 2025.02 23-feb-2025 uncounted \
|
||||
6CCE29206AC9 VENDOR_STRING=LSC_CTL_PROPBLD HOSTID=f81234567890
|
||||
FEATURE LSC_CTL_PROPSDK_PFR lattice 2025.02 23-feb-2025 uncounted \
|
||||
7FE203087494 VENDOR_STRING=LSC_CTL_PROPSDK_PFR \
|
||||
HOSTID=f81234567890
|
||||
|
||||
DAEMON mgcld path_to_mgcld
|
||||
INCREMENT latticemsim mgcld 2024.10 23-feb-2025 uncounted \
|
||||
FFD823285F6785FED8B2 VENDOR_STRING=7E796EE6 HOSTID=f81234567890 \
|
||||
ISSUER="ModelSIM/Questa Lattice" SN=290985235 SIGN2="1A3D 76F8 48B1 \
|
||||
B3C0 D0C7 8C20 1131 C772 AB70 A5D3 D7F0 6B5F F4F7 70F8 F1D6 040C E65F \
|
||||
8CE4 CF22 E322 D961 988D 6A4C 5806 B9EC D64A 3D9F 1F16 260B AB10"
|
||||
|
289
hw/pcb/LICENSE
Normal file
289
hw/pcb/LICENSE
Normal file
@ -0,0 +1,289 @@
|
||||
CERN Open Hardware Licence Version 2 - Strongly Reciprocal
|
||||
|
||||
|
||||
Preamble
|
||||
|
||||
CERN has developed this licence to promote collaboration among
|
||||
hardware designers and to provide a legal tool which supports the
|
||||
freedom to use, study, modify, share and distribute hardware designs
|
||||
and products based on those designs. Version 2 of the CERN Open
|
||||
Hardware Licence comes in three variants: CERN-OHL-P (permissive); and
|
||||
two reciprocal licences: CERN-OHL-W (weakly reciprocal) and this
|
||||
licence, CERN-OHL-S (strongly reciprocal).
|
||||
|
||||
The CERN-OHL-S is copyright CERN 2020. Anyone is welcome to use it, in
|
||||
unmodified form only.
|
||||
|
||||
Use of this Licence does not imply any endorsement by CERN of any
|
||||
Licensor or their designs nor does it imply any involvement by CERN in
|
||||
their development.
|
||||
|
||||
|
||||
1 Definitions
|
||||
|
||||
1.1 'Licence' means this CERN-OHL-S.
|
||||
|
||||
1.2 'Compatible Licence' means
|
||||
|
||||
a) any earlier version of the CERN Open Hardware licence, or
|
||||
|
||||
b) any version of the CERN-OHL-S, or
|
||||
|
||||
c) any licence which permits You to treat the Source to which
|
||||
it applies as licensed under CERN-OHL-S provided that on
|
||||
Conveyance of any such Source, or any associated Product You
|
||||
treat the Source in question as being licensed under
|
||||
CERN-OHL-S.
|
||||
|
||||
1.3 'Source' means information such as design materials or digital
|
||||
code which can be applied to Make or test a Product or to
|
||||
prepare a Product for use, Conveyance or sale, regardless of its
|
||||
medium or how it is expressed. It may include Notices.
|
||||
|
||||
1.4 'Covered Source' means Source that is explicitly made available
|
||||
under this Licence.
|
||||
|
||||
1.5 'Product' means any device, component, work or physical object,
|
||||
whether in finished or intermediate form, arising from the use,
|
||||
application or processing of Covered Source.
|
||||
|
||||
1.6 'Make' means to create or configure something, whether by
|
||||
manufacture, assembly, compiling, loading or applying Covered
|
||||
Source or another Product or otherwise.
|
||||
|
||||
1.7 'Available Component' means any part, sub-assembly, library or
|
||||
code which:
|
||||
|
||||
a) is licensed to You as Complete Source under a Compatible
|
||||
Licence; or
|
||||
|
||||
b) is available, at the time a Product or the Source containing
|
||||
it is first Conveyed, to You and any other prospective
|
||||
licensees
|
||||
|
||||
i) as a physical part with sufficient rights and
|
||||
information (including any configuration and
|
||||
programming files and information about its
|
||||
characteristics and interfaces) to enable it either to
|
||||
be Made itself, or to be sourced and used to Make the
|
||||
Product; or
|
||||
ii) as part of the normal distribution of a tool used to
|
||||
design or Make the Product.
|
||||
|
||||
1.8 'Complete Source' means the set of all Source necessary to Make
|
||||
a Product, in the preferred form for making modifications,
|
||||
including necessary installation and interfacing information
|
||||
both for the Product, and for any included Available Components.
|
||||
If the format is proprietary, it must also be made available in
|
||||
a format (if the proprietary tool can create it) which is
|
||||
viewable with a tool available to potential licensees and
|
||||
licensed under a licence approved by the Free Software
|
||||
Foundation or the Open Source Initiative. Complete Source need
|
||||
not include the Source of any Available Component, provided that
|
||||
You include in the Complete Source sufficient information to
|
||||
enable a recipient to Make or source and use the Available
|
||||
Component to Make the Product.
|
||||
|
||||
1.9 'Source Location' means a location where a Licensor has placed
|
||||
Covered Source, and which that Licensor reasonably believes will
|
||||
remain easily accessible for at least three years for anyone to
|
||||
obtain a digital copy.
|
||||
|
||||
1.10 'Notice' means copyright, acknowledgement and trademark notices,
|
||||
Source Location references, modification notices (subsection
|
||||
3.3(b)) and all notices that refer to this Licence and to the
|
||||
disclaimer of warranties that are included in the Covered
|
||||
Source.
|
||||
|
||||
1.11 'Licensee' or 'You' means any person exercising rights under
|
||||
this Licence.
|
||||
|
||||
1.12 'Licensor' means a natural or legal person who creates or
|
||||
modifies Covered Source. A person may be a Licensee and a
|
||||
Licensor at the same time.
|
||||
|
||||
1.13 'Convey' means to communicate to the public or distribute.
|
||||
|
||||
|
||||
2 Applicability
|
||||
|
||||
2.1 This Licence governs the use, copying, modification, Conveying
|
||||
of Covered Source and Products, and the Making of Products. By
|
||||
exercising any right granted under this Licence, You irrevocably
|
||||
accept these terms and conditions.
|
||||
|
||||
2.2 This Licence is granted by the Licensor directly to You, and
|
||||
shall apply worldwide and without limitation in time.
|
||||
|
||||
2.3 You shall not attempt to restrict by contract or otherwise the
|
||||
rights granted under this Licence to other Licensees.
|
||||
|
||||
2.4 This Licence is not intended to restrict fair use, fair dealing,
|
||||
or any other similar right.
|
||||
|
||||
|
||||
3 Copying, Modifying and Conveying Covered Source
|
||||
|
||||
3.1 You may copy and Convey verbatim copies of Covered Source, in
|
||||
any medium, provided You retain all Notices.
|
||||
|
||||
3.2 You may modify Covered Source, other than Notices, provided that
|
||||
You irrevocably undertake to make that modified Covered Source
|
||||
available from a Source Location should You Convey a Product in
|
||||
circumstances where the recipient does not otherwise receive a
|
||||
copy of the modified Covered Source. In each case subsection 3.3
|
||||
shall apply.
|
||||
|
||||
You may only delete Notices if they are no longer applicable to
|
||||
the corresponding Covered Source as modified by You and You may
|
||||
add additional Notices applicable to Your modifications.
|
||||
Including Covered Source in a larger work is modifying the
|
||||
Covered Source, and the larger work becomes modified Covered
|
||||
Source.
|
||||
|
||||
3.3 You may Convey modified Covered Source (with the effect that You
|
||||
shall also become a Licensor) provided that You:
|
||||
|
||||
a) retain Notices as required in subsection 3.2;
|
||||
|
||||
b) add a Notice to the modified Covered Source stating that You
|
||||
have modified it, with the date and brief description of how
|
||||
You have modified it;
|
||||
|
||||
c) add a Source Location Notice for the modified Covered Source
|
||||
if You Convey in circumstances where the recipient does not
|
||||
otherwise receive a copy of the modified Covered Source; and
|
||||
|
||||
d) license the modified Covered Source under the terms and
|
||||
conditions of this Licence (or, as set out in subsection
|
||||
8.3, a later version, if permitted by the licence of the
|
||||
original Covered Source). Such modified Covered Source must
|
||||
be licensed as a whole, but excluding Available Components
|
||||
contained in it, which remain licensed under their own
|
||||
applicable licences.
|
||||
|
||||
|
||||
4 Making and Conveying Products
|
||||
|
||||
You may Make Products, and/or Convey them, provided that You either
|
||||
provide each recipient with a copy of the Complete Source or ensure
|
||||
that each recipient is notified of the Source Location of the Complete
|
||||
Source. That Complete Source is Covered Source, and You must
|
||||
accordingly satisfy Your obligations set out in subsection 3.3. If
|
||||
specified in a Notice, the Product must visibly and securely display
|
||||
the Source Location on it or its packaging or documentation in the
|
||||
manner specified in that Notice.
|
||||
|
||||
|
||||
5 Research and Development
|
||||
|
||||
You may Convey Covered Source, modified Covered Source or Products to
|
||||
a legal entity carrying out development, testing or quality assurance
|
||||
work on Your behalf provided that the work is performed on terms which
|
||||
prevent the entity from both using the Source or Products for its own
|
||||
internal purposes and Conveying the Source or Products or any
|
||||
modifications to them to any person other than You. Any modifications
|
||||
made by the entity shall be deemed to be made by You pursuant to
|
||||
subsection 3.2.
|
||||
|
||||
|
||||
6 DISCLAIMER AND LIABILITY
|
||||
|
||||
6.1 DISCLAIMER OF WARRANTY -- The Covered Source and any Products
|
||||
are provided 'as is' and any express or implied warranties,
|
||||
including, but not limited to, implied warranties of
|
||||
merchantability, of satisfactory quality, non-infringement of
|
||||
third party rights, and fitness for a particular purpose or use
|
||||
are disclaimed in respect of any Source or Product to the
|
||||
maximum extent permitted by law. The Licensor makes no
|
||||
representation that any Source or Product does not or will not
|
||||
infringe any patent, copyright, trade secret or other
|
||||
proprietary right. The entire risk as to the use, quality, and
|
||||
performance of any Source or Product shall be with You and not
|
||||
the Licensor. This disclaimer of warranty is an essential part
|
||||
of this Licence and a condition for the grant of any rights
|
||||
granted under this Licence.
|
||||
|
||||
6.2 EXCLUSION AND LIMITATION OF LIABILITY -- The Licensor shall, to
|
||||
the maximum extent permitted by law, have no liability for
|
||||
direct, indirect, special, incidental, consequential, exemplary,
|
||||
punitive or other damages of any character including, without
|
||||
limitation, procurement of substitute goods or services, loss of
|
||||
use, data or profits, or business interruption, however caused
|
||||
and on any theory of contract, warranty, tort (including
|
||||
negligence), product liability or otherwise, arising in any way
|
||||
in relation to the Covered Source, modified Covered Source
|
||||
and/or the Making or Conveyance of a Product, even if advised of
|
||||
the possibility of such damages, and You shall hold the
|
||||
Licensor(s) free and harmless from any liability, costs,
|
||||
damages, fees and expenses, including claims by third parties,
|
||||
in relation to such use.
|
||||
|
||||
|
||||
7 Patents
|
||||
|
||||
7.1 Subject to the terms and conditions of this Licence, each
|
||||
Licensor hereby grants to You a perpetual, worldwide,
|
||||
non-exclusive, no-charge, royalty-free, irrevocable (except as
|
||||
stated in subsections 7.2 and 8.4) patent licence to Make, have
|
||||
Made, use, offer to sell, sell, import, and otherwise transfer
|
||||
the Covered Source and Products, where such licence applies only
|
||||
to those patent claims licensable by such Licensor that are
|
||||
necessarily infringed by exercising rights under the Covered
|
||||
Source as Conveyed by that Licensor.
|
||||
|
||||
7.2 If You institute patent litigation against any entity (including
|
||||
a cross-claim or counterclaim in a lawsuit) alleging that the
|
||||
Covered Source or a Product constitutes direct or contributory
|
||||
patent infringement, or You seek any declaration that a patent
|
||||
licensed to You under this Licence is invalid or unenforceable
|
||||
then any rights granted to You under this Licence shall
|
||||
terminate as of the date such process is initiated.
|
||||
|
||||
|
||||
8 General
|
||||
|
||||
8.1 If any provisions of this Licence are or subsequently become
|
||||
invalid or unenforceable for any reason, the remaining
|
||||
provisions shall remain effective.
|
||||
|
||||
8.2 You shall not use any of the name (including acronyms and
|
||||
abbreviations), image, or logo by which the Licensor or CERN is
|
||||
known, except where needed to comply with section 3, or where
|
||||
the use is otherwise allowed by law. Any such permitted use
|
||||
shall be factual and shall not be made so as to suggest any kind
|
||||
of endorsement or implication of involvement by the Licensor or
|
||||
its personnel.
|
||||
|
||||
8.3 CERN may publish updated versions and variants of this Licence
|
||||
which it considers to be in the spirit of this version, but may
|
||||
differ in detail to address new problems or concerns. New
|
||||
versions will be published with a unique version number and a
|
||||
variant identifier specifying the variant. If the Licensor has
|
||||
specified that a given variant applies to the Covered Source
|
||||
without specifying a version, You may treat that Covered Source
|
||||
as being released under any version of the CERN-OHL with that
|
||||
variant. If no variant is specified, the Covered Source shall be
|
||||
treated as being released under CERN-OHL-S. The Licensor may
|
||||
also specify that the Covered Source is subject to a specific
|
||||
version of the CERN-OHL or any later version in which case You
|
||||
may apply this or any later version of CERN-OHL with the same
|
||||
variant identifier published by CERN.
|
||||
|
||||
8.4 This Licence shall terminate with immediate effect if You fail
|
||||
to comply with any of its terms and conditions.
|
||||
|
||||
8.5 However, if You cease all breaches of this Licence, then Your
|
||||
Licence from any Licensor is reinstated unless such Licensor has
|
||||
terminated this Licence by giving You, while You remain in
|
||||
breach, a notice specifying the breach and requiring You to cure
|
||||
it within 30 days, and You have failed to come into compliance
|
||||
in all material respects by the end of the 30 day period. Should
|
||||
You repeat the breach after receipt of a cure notice and
|
||||
subsequent reinstatement, this Licence will terminate
|
||||
immediately and permanently. Section 6 shall continue to apply
|
||||
after any termination.
|
||||
|
||||
8.6 This Licence shall not be enforceable except by a Licensor
|
||||
acting as such, and third party beneficiary rights are
|
||||
specifically excluded.
|
@ -9,8 +9,8 @@
|
||||
(paper "A4")
|
||||
(title_block
|
||||
(title "SummerCart64")
|
||||
(date "2024-08-11")
|
||||
(rev "2.1")
|
||||
(date "2025-03-10")
|
||||
(rev "2.1a")
|
||||
(company "Mateusz Faderewski")
|
||||
)
|
||||
(layers
|
||||
@ -26785,6 +26785,18 @@
|
||||
(layer "User.1")
|
||||
(uuid "e23fcef0-be99-4588-9e98-11fe6a1e9f26")
|
||||
)
|
||||
(gr_text "Designed by Mateusz Faderewski\nLicensed under CERN-OHL-S-2.0\nhttps://summercart64.dev"
|
||||
(at 198 122.2 0)
|
||||
(layer "B.SilkS")
|
||||
(uuid "9c42ed30-fd86-4d8a-bb4f-89dcb0e60e6e")
|
||||
(effects
|
||||
(font
|
||||
(size 1 1)
|
||||
(thickness 0.15)
|
||||
)
|
||||
(justify left bottom mirror)
|
||||
)
|
||||
)
|
||||
(gr_text "RX"
|
||||
(at 123 84.75 90)
|
||||
(layer "F.SilkS")
|
||||
@ -26807,7 +26819,7 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
(gr_text "HW ver: 2.1"
|
||||
(gr_text "HW ver: 2.1a"
|
||||
(at 100.25 83.5 0)
|
||||
(layer "F.SilkS")
|
||||
(uuid "4ab65223-ff63-4a7c-853c-e7b8d8e12e76")
|
||||
@ -26819,7 +26831,7 @@
|
||||
(justify left)
|
||||
)
|
||||
)
|
||||
(gr_text "11.08.2024"
|
||||
(gr_text "10.03.2025"
|
||||
(at 100.25 85.25 0)
|
||||
(layer "F.SilkS")
|
||||
(uuid "4ebec920-49dd-4966-8b6b-a500a99cc601")
|
||||
@ -26842,17 +26854,6 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
(gr_text "Licensed under GNU GPLv3\nhttps://summercart64.dev"
|
||||
(at 137 120 0)
|
||||
(layer "F.SilkS")
|
||||
(uuid "77e7f556-c63c-46bb-bff0-b858da0827cb")
|
||||
(effects
|
||||
(font
|
||||
(size 0.95 0.95)
|
||||
(thickness 0.15)
|
||||
)
|
||||
)
|
||||
)
|
||||
(gr_text "3V3"
|
||||
(at 135 116 0)
|
||||
(layer "F.SilkS")
|
||||
@ -26887,19 +26888,6 @@
|
||||
(justify left)
|
||||
)
|
||||
)
|
||||
(gr_text "© 2020 - 2024\nMateusz Faderewski"
|
||||
(at 200.1 78.8 0)
|
||||
(layer "F.Mask")
|
||||
(uuid "9c42ed30-fd86-4d8a-bb4f-89dcb0e60e6e")
|
||||
(effects
|
||||
(font
|
||||
(size 1 1)
|
||||
(thickness 0.25)
|
||||
(bold yes)
|
||||
)
|
||||
(justify right top)
|
||||
)
|
||||
)
|
||||
(segment
|
||||
(start 172.325 85.075)
|
||||
(end 172.25 85)
|
||||
|
@ -6,8 +6,8 @@
|
||||
(paper "A2")
|
||||
(title_block
|
||||
(title "SummerCart64")
|
||||
(date "2024-08-11")
|
||||
(rev "2.1")
|
||||
(date "2025-03-10")
|
||||
(rev "2.1a")
|
||||
(company "Mateusz Faderewski")
|
||||
)
|
||||
(lib_symbols
|
||||
@ -19434,7 +19434,6 @@
|
||||
)
|
||||
(label "USB_D+"
|
||||
(at 97.79 124.46 0)
|
||||
(fields_autoplaced yes)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
@ -19445,7 +19444,6 @@
|
||||
)
|
||||
(label "USB_D-"
|
||||
(at 97.79 121.92 0)
|
||||
(fields_autoplaced yes)
|
||||
(effects
|
||||
(font
|
||||
(size 1.27 1.27)
|
||||
|
2
hw/pcb/sc64v2_bom.html
generated
2
hw/pcb/sc64v2_bom.html
generated
File diff suppressed because one or more lines are too long
@ -34,6 +34,7 @@ SRC_FILES = \
|
||||
interrupts.c \
|
||||
interrupts.S \
|
||||
io.c \
|
||||
joybus.c \
|
||||
main.c \
|
||||
menu.c \
|
||||
reboot.S \
|
||||
|
@ -79,6 +79,7 @@ void boot (boot_params_t *params) {
|
||||
while (cpu_io_read(&SP->DMA_BUSY));
|
||||
|
||||
cpu_io_write(&PI->SR, PI_SR_CLR_INTR | PI_SR_RESET);
|
||||
cpu_io_write(&SI->SR, 0);
|
||||
while ((cpu_io_read(&VI->CURR_LINE) & ~(VI_CURR_LINE_FIELD)) != 0);
|
||||
cpu_io_write(&VI->V_INTR, 0x3FF);
|
||||
cpu_io_write(&VI->H_LIMITS, 0);
|
||||
|
@ -116,8 +116,8 @@ static void display_draw_character (char c) {
|
||||
}
|
||||
|
||||
if ((char_x + FONT_WIDTH) > (SCREEN_WIDTH - BORDER_WIDTH)) {
|
||||
char_x = BORDER_WIDTH;
|
||||
char_y += FONT_HEIGHT + LINE_SPACING;
|
||||
char_x -= FONT_WIDTH;
|
||||
c = '\x7F';
|
||||
}
|
||||
|
||||
if ((c < ' ') || (c > '~')) {
|
||||
@ -185,7 +185,7 @@ bool display_ready (void) {
|
||||
}
|
||||
|
||||
void display_vprintf (const char *fmt, va_list args) {
|
||||
char line[256];
|
||||
char line[1024];
|
||||
|
||||
vsniprintf(line, sizeof(line), fmt, args);
|
||||
display_draw_string(line);
|
||||
|
@ -48,8 +48,7 @@ void exception_fatal_handler (uint32_t exception_code, exception_t *e) {
|
||||
display_printf(" s4: 0x%08lX s5: 0x%08lX s6: 0x%08lX s7: 0x%08lX\n", e->s4.u32, e->s5.u32, e->s6.u32, e->s7.u32);
|
||||
display_printf(" t8: 0x%08lX t9: 0x%08lX k0: 0x%08lX k1: 0x%08lX\n", e->t8.u32, e->t9.u32, e->k0.u32, e->k1.u32);
|
||||
display_printf(" gp: 0x%08lX sp: 0x%08lX s8: 0x%08lX ra: 0x%08lX\n", e->gp.u32, e->sp.u32, e->s8.u32, e->ra.u32);
|
||||
display_printf(" hi: 0x%016lX\n", e->hi.u64);
|
||||
display_printf(" lo: 0x%016lX\n", e->lo.u64);
|
||||
display_printf(" hi: 0x%016lX lo: 0x%016lX\n", e->hi.u64, e->lo.u64);
|
||||
|
||||
while (true);
|
||||
}
|
||||
|
16
sw/bootloader/src/fatfs/00history.txt
vendored
16
sw/bootloader/src/fatfs/00history.txt
vendored
@ -367,3 +367,19 @@ R0.15 (November 6, 2022)
|
||||
Fixed string functions cannot write the unicode characters not in BMP when FF_LFN_UNICODE == 2 (UTF-8).
|
||||
Fixed a compatibility issue in identification of GPT header.
|
||||
|
||||
|
||||
|
||||
R0.15a (November 22, 2024)
|
||||
Fixed a complie error when FF_FS_LOCK != 0.
|
||||
Fixed a potential issue when work FatFs concurrency with FF_FS_REENTRANT, FF_VOLUMES >= 2 and FF_FS_LOCK > 0.
|
||||
Made f_setlabel() accept a volume label in Unix style volume ID when FF_STR_VOLUME_ID == 2.
|
||||
Made FatFs update PercInUse field in exFAT VBR. (A preceding f_getfree() is needed for the accuracy)
|
||||
|
||||
1. January 9, 2025
|
||||
--------------------------------------------------------------------------------------------------
|
||||
FatFs fails to load the FsInfo in FAT32 volumes and the f_getfree function will always be forced
|
||||
a full FAT scan which takes a long time.
|
||||
|
||||
This problem was appeared at R0.15a and reported via an e-mail.
|
||||
|
||||
To fix this problem, apply ff15a_p1.diff to the ff.c.
|
||||
|
@ -50,6 +50,9 @@ DRESULT disk_read (BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) {
|
||||
if (pdrv > 0) {
|
||||
return RES_PARERR;
|
||||
}
|
||||
if ((sector + count) > 0x100000000ULL) {
|
||||
return RES_PARERR;
|
||||
}
|
||||
uint32_t *physical_address = (uint32_t *) (PHYSICAL(buff));
|
||||
if (physical_address < (uint32_t *) (N64_RAM_SIZE)) {
|
||||
uint8_t aligned_buffer[BUFFER_BLOCKS_MAX * SD_SECTOR_SIZE] __attribute__((aligned(8)));
|
||||
@ -82,6 +85,9 @@ DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count) {
|
||||
if (pdrv > 0) {
|
||||
return RES_PARERR;
|
||||
}
|
||||
if ((sector + count) > 0x100000000ULL) {
|
||||
return RES_PARERR;
|
||||
}
|
||||
uint32_t *physical_address = (uint32_t *) (PHYSICAL(buff));
|
||||
if (physical_address < (uint32_t *) (N64_RAM_SIZE)) {
|
||||
uint8_t aligned_buffer[BUFFER_BLOCKS_MAX * SD_SECTOR_SIZE] __attribute__((aligned(8)));
|
||||
|
734
sw/bootloader/src/fatfs/ff.c
vendored
734
sw/bootloader/src/fatfs/ff.c
vendored
File diff suppressed because it is too large
Load Diff
67
sw/bootloader/src/fatfs/ff.h
vendored
67
sw/bootloader/src/fatfs/ff.h
vendored
@ -1,8 +1,8 @@
|
||||
/*----------------------------------------------------------------------------/
|
||||
/ FatFs - Generic FAT Filesystem module R0.15 /
|
||||
/ FatFs - Generic FAT Filesystem module R0.15a /
|
||||
/-----------------------------------------------------------------------------/
|
||||
/
|
||||
/ Copyright (C) 2022, ChaN, all right reserved.
|
||||
/ Copyright (C) 2024, ChaN, all right reserved.
|
||||
/
|
||||
/ FatFs module is an open source software. Redistribution and use of FatFs in
|
||||
/ source and binary forms, with or without modification, are permitted provided
|
||||
@ -20,14 +20,15 @@
|
||||
|
||||
|
||||
#ifndef FF_DEFINED
|
||||
#define FF_DEFINED 80286 /* Revision ID */
|
||||
#define FF_DEFINED 5380 /* Revision ID */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if !defined(FFCONF_DEF)
|
||||
#include "ffconf.h" /* FatFs configuration options */
|
||||
|
||||
#endif
|
||||
#if FF_DEFINED != FFCONF_DEF
|
||||
#error Wrong configuration file (ffconf.h).
|
||||
#endif
|
||||
@ -48,18 +49,18 @@ typedef unsigned __int64 QWORD;
|
||||
#include <stdint.h>
|
||||
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
|
||||
typedef unsigned char BYTE; /* char must be 8-bit */
|
||||
typedef uint16_t WORD; /* 16-bit unsigned integer */
|
||||
typedef uint32_t DWORD; /* 32-bit unsigned integer */
|
||||
typedef uint64_t QWORD; /* 64-bit unsigned integer */
|
||||
typedef WORD WCHAR; /* UTF-16 character type */
|
||||
typedef uint16_t WORD; /* 16-bit unsigned */
|
||||
typedef uint32_t DWORD; /* 32-bit unsigned */
|
||||
typedef uint64_t QWORD; /* 64-bit unsigned */
|
||||
typedef WORD WCHAR; /* UTF-16 code unit */
|
||||
|
||||
#else /* Earlier than C99 */
|
||||
#define FF_INTDEF 1
|
||||
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
|
||||
typedef unsigned char BYTE; /* char must be 8-bit */
|
||||
typedef unsigned short WORD; /* 16-bit unsigned integer */
|
||||
typedef unsigned long DWORD; /* 32-bit unsigned integer */
|
||||
typedef WORD WCHAR; /* UTF-16 character type */
|
||||
typedef unsigned short WORD; /* short must be 16-bit */
|
||||
typedef unsigned long DWORD; /* long must be 32-bit */
|
||||
typedef WORD WCHAR; /* UTF-16 code unit */
|
||||
#endif
|
||||
|
||||
|
||||
@ -113,15 +114,15 @@ typedef char TCHAR;
|
||||
|
||||
#if FF_MULTI_PARTITION /* Multiple partition configuration */
|
||||
typedef struct {
|
||||
BYTE pd; /* Physical drive number */
|
||||
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
|
||||
BYTE pd; /* Associated physical drive */
|
||||
BYTE pt; /* Associated partition (0:Auto detect, 1-4:Forced partition) */
|
||||
} PARTITION;
|
||||
extern PARTITION VolToPart[]; /* Volume - Partition mapping table */
|
||||
extern PARTITION VolToPart[]; /* Volume to partition mapping table */
|
||||
#endif
|
||||
|
||||
#if FF_STR_VOLUME_ID
|
||||
#ifndef FF_VOLUME_STRS
|
||||
extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */
|
||||
extern const char* VolumeStr[FF_VOLUMES]; /* User defined volume ID table */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -130,12 +131,12 @@ extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */
|
||||
/* Filesystem object structure (FATFS) */
|
||||
|
||||
typedef struct {
|
||||
BYTE fs_type; /* Filesystem type (0:not mounted) */
|
||||
BYTE fs_type; /* Filesystem type (0:blank filesystem object) */
|
||||
BYTE pdrv; /* Volume hosting physical drive */
|
||||
BYTE ldrv; /* Logical drive number (used only when FF_FS_REENTRANT) */
|
||||
BYTE n_fats; /* Number of FATs (1 or 2) */
|
||||
BYTE wflag; /* win[] status (b0:dirty) */
|
||||
BYTE fsi_flag; /* FSINFO status (b7:disabled, b0:dirty) */
|
||||
BYTE wflag; /* win[] status (1:dirty) */
|
||||
BYTE fsi_flag; /* Allocation information control (b7:disabled, b0:dirty) */
|
||||
WORD id; /* Volume mount ID */
|
||||
WORD n_rootdir; /* Number of root directory entries (FAT12/16) */
|
||||
WORD csize; /* Cluster size [sectors] */
|
||||
@ -146,11 +147,11 @@ typedef struct {
|
||||
WCHAR* lfnbuf; /* LFN working buffer */
|
||||
#endif
|
||||
#if FF_FS_EXFAT
|
||||
BYTE* dirbuf; /* Directory entry block scratchpad buffer for exFAT */
|
||||
BYTE* dirbuf; /* Directory entry block scratch pad buffer for exFAT */
|
||||
#endif
|
||||
#if !FF_FS_READONLY
|
||||
DWORD last_clst; /* Last allocated cluster */
|
||||
DWORD free_clst; /* Number of free clusters */
|
||||
DWORD last_clst; /* Last allocated cluster (Unknown if >= n_fatent) */
|
||||
DWORD free_clst; /* Number of free clusters (Unknown if >= n_fatent-2) */
|
||||
#endif
|
||||
#if FF_FS_RPATH
|
||||
DWORD cdir; /* Current directory start cluster (0:root) */
|
||||
@ -272,24 +273,24 @@ typedef struct {
|
||||
/* File function return code (FRESULT) */
|
||||
|
||||
typedef enum {
|
||||
FR_OK = 0, /* (0) Succeeded */
|
||||
FR_OK = 0, /* (0) Function succeeded */
|
||||
FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */
|
||||
FR_INT_ERR, /* (2) Assertion failed */
|
||||
FR_NOT_READY, /* (3) The physical drive cannot work */
|
||||
FR_NOT_READY, /* (3) The physical drive does not work */
|
||||
FR_NO_FILE, /* (4) Could not find the file */
|
||||
FR_NO_PATH, /* (5) Could not find the path */
|
||||
FR_INVALID_NAME, /* (6) The path name format is invalid */
|
||||
FR_DENIED, /* (7) Access denied due to prohibited access or directory full */
|
||||
FR_EXIST, /* (8) Access denied due to prohibited access */
|
||||
FR_DENIED, /* (7) Access denied due to a prohibited access or directory full */
|
||||
FR_EXIST, /* (8) Access denied due to a prohibited access */
|
||||
FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */
|
||||
FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */
|
||||
FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */
|
||||
FR_NOT_ENABLED, /* (12) The volume has no work area */
|
||||
FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */
|
||||
FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any problem */
|
||||
FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */
|
||||
FR_NO_FILESYSTEM, /* (13) Could not find a valid FAT volume */
|
||||
FR_MKFS_ABORTED, /* (14) The f_mkfs function aborted due to some problem */
|
||||
FR_TIMEOUT, /* (15) Could not take control of the volume within defined period */
|
||||
FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */
|
||||
FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */
|
||||
FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated or given buffer is insufficient in size */
|
||||
FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > FF_FS_LOCK */
|
||||
FR_INVALID_PARAMETER /* (19) Given parameter is invalid */
|
||||
} FRESULT;
|
||||
@ -375,7 +376,7 @@ DWORD ff_wtoupper (DWORD uni); /* Unicode upper-case conversion */
|
||||
void* ff_memalloc (UINT msize); /* Allocate memory block */
|
||||
void ff_memfree (void* mblock); /* Free memory block */
|
||||
#endif
|
||||
#if FF_FS_REENTRANT /* Sync functions */
|
||||
#if FF_FS_REENTRANT /* Sync functions */
|
||||
int ff_mutex_create (int vol); /* Create a sync object */
|
||||
void ff_mutex_delete (int vol); /* Delete a sync object */
|
||||
int ff_mutex_take (int vol); /* Lock sync object */
|
||||
@ -389,7 +390,7 @@ void ff_mutex_give (int vol); /* Unlock sync object */
|
||||
/* Flags and Offset Address */
|
||||
/*--------------------------------------------------------------*/
|
||||
|
||||
/* File access mode and open method flags (3rd argument of f_open) */
|
||||
/* File access mode and open method flags (3rd argument of f_open function) */
|
||||
#define FA_READ 0x01
|
||||
#define FA_WRITE 0x02
|
||||
#define FA_OPEN_EXISTING 0x00
|
||||
@ -398,10 +399,10 @@ void ff_mutex_give (int vol); /* Unlock sync object */
|
||||
#define FA_OPEN_ALWAYS 0x10
|
||||
#define FA_OPEN_APPEND 0x30
|
||||
|
||||
/* Fast seek controls (2nd argument of f_lseek) */
|
||||
/* Fast seek controls (2nd argument of f_lseek function) */
|
||||
#define CREATE_LINKMAP ((FSIZE_t)0 - 1)
|
||||
|
||||
/* Format options (2nd argument of f_mkfs) */
|
||||
/* Format options (2nd argument of f_mkfs function) */
|
||||
#define FM_FAT 0x01
|
||||
#define FM_FAT32 0x02
|
||||
#define FM_EXFAT 0x04
|
||||
|
76
sw/bootloader/src/fatfs/ffconf.h
vendored
76
sw/bootloader/src/fatfs/ffconf.h
vendored
@ -2,7 +2,7 @@
|
||||
/ Configurations of FatFs Module
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FFCONF_DEF 80286 /* Revision ID */
|
||||
#define FFCONF_DEF 5380 /* Revision ID */
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Function Configurations
|
||||
@ -31,45 +31,45 @@
|
||||
|
||||
|
||||
#define FF_USE_MKFS 1
|
||||
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
|
||||
/* This option switches f_mkfs(). (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define FF_USE_FASTSEEK 0
|
||||
/* This option switches fast seek function. (0:Disable or 1:Enable) */
|
||||
/* This option switches fast seek feature. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define FF_USE_EXPAND 0
|
||||
/* This option switches f_expand function. (0:Disable or 1:Enable) */
|
||||
/* This option switches f_expand(). (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define FF_USE_CHMOD 0
|
||||
/* This option switches attribute manipulation functions, f_chmod() and f_utime().
|
||||
/* This option switches attribute control API functions, f_chmod() and f_utime().
|
||||
/ (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */
|
||||
|
||||
|
||||
#define FF_USE_LABEL 1
|
||||
/* This option switches volume label functions, f_getlabel() and f_setlabel().
|
||||
/* This option switches volume label API functions, f_getlabel() and f_setlabel().
|
||||
/ (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define FF_USE_FORWARD 0
|
||||
/* This option switches f_forward() function. (0:Disable or 1:Enable) */
|
||||
/* This option switches f_forward(). (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define FF_USE_STRFUNC 0
|
||||
#define FF_PRINT_LLI 1
|
||||
#define FF_PRINT_FLOAT 1
|
||||
#define FF_PRINT_LLI 0
|
||||
#define FF_PRINT_FLOAT 0
|
||||
#define FF_STRF_ENCODE 3
|
||||
/* FF_USE_STRFUNC switches string functions, f_gets(), f_putc(), f_puts() and
|
||||
/ f_printf().
|
||||
/* FF_USE_STRFUNC switches the string API functions, f_gets(), f_putc(), f_puts()
|
||||
/ and f_printf().
|
||||
/
|
||||
/ 0: Disable. FF_PRINT_LLI, FF_PRINT_FLOAT and FF_STRF_ENCODE have no effect.
|
||||
/ 1: Enable without LF-CRLF conversion.
|
||||
/ 2: Enable with LF-CRLF conversion.
|
||||
/ 1: Enable without LF - CRLF conversion.
|
||||
/ 2: Enable with LF - CRLF conversion.
|
||||
/
|
||||
/ FF_PRINT_LLI = 1 makes f_printf() support long long argument and FF_PRINT_FLOAT = 1/2
|
||||
/ makes f_printf() support floating point argument. These features want C99 or later.
|
||||
/ When FF_LFN_UNICODE >= 1 with LFN enabled, string functions convert the character
|
||||
/ When FF_LFN_UNICODE >= 1 with LFN enabled, string API functions convert the character
|
||||
/ encoding in it. FF_STRF_ENCODE selects assumption of character encoding ON THE FILE
|
||||
/ to be read/written via those functions.
|
||||
/
|
||||
@ -118,15 +118,15 @@
|
||||
/* The FF_USE_LFN switches the support for LFN (long file name).
|
||||
/
|
||||
/ 0: Disable LFN. FF_MAX_LFN has no effect.
|
||||
/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
|
||||
/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
|
||||
/ 2: Enable LFN with dynamic working buffer on the STACK.
|
||||
/ 3: Enable LFN with dynamic working buffer on the HEAP.
|
||||
/
|
||||
/ To enable the LFN, ffunicode.c needs to be added to the project. The LFN function
|
||||
/ To enable the LFN, ffunicode.c needs to be added to the project. The LFN feature
|
||||
/ requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
|
||||
/ additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
|
||||
/ The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
|
||||
/ be in range of 12 to 255. It is recommended to be set it 255 to fully support LFN
|
||||
/ be in range of 12 to 255. It is recommended to be set 255 to fully support the LFN
|
||||
/ specification.
|
||||
/ When use stack for the working buffer, take care on stack overflow. When use heap
|
||||
/ memory for the working buffer, memory management functions, ff_memalloc() and
|
||||
@ -145,7 +145,7 @@
|
||||
/ When LFN is not enabled, this option has no effect. */
|
||||
|
||||
|
||||
#define FF_LFN_BUF 1023
|
||||
#define FF_LFN_BUF 255
|
||||
#define FF_SFN_BUF 12
|
||||
/* This set of options defines size of file name members in the FILINFO structure
|
||||
/ which is used to read out directory items. These values should be suffcient for
|
||||
@ -156,9 +156,9 @@
|
||||
#define FF_FS_RPATH 0
|
||||
/* This option configures support for relative path.
|
||||
/
|
||||
/ 0: Disable relative path and remove related functions.
|
||||
/ 0: Disable relative path and remove related API functions.
|
||||
/ 1: Enable relative path. f_chdir() and f_chdrive() are available.
|
||||
/ 2: f_getcwd() function is available in addition to 1.
|
||||
/ 2: f_getcwd() is available in addition to 1.
|
||||
*/
|
||||
|
||||
|
||||
@ -175,7 +175,7 @@
|
||||
/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings.
|
||||
/ When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive
|
||||
/ number in the path name. FF_VOLUME_STRS defines the volume ID strings for each
|
||||
/ logical drives. Number of items must not be less than FF_VOLUMES. Valid
|
||||
/ logical drive. Number of items must not be less than FF_VOLUMES. Valid
|
||||
/ characters for the volume ID strings are A-Z, a-z and 0-9, however, they are
|
||||
/ compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is
|
||||
/ not defined, a user defined volume string table is needed as:
|
||||
@ -188,9 +188,9 @@
|
||||
/* This option switches support for multiple volumes on the physical drive.
|
||||
/ By default (0), each logical drive number is bound to the same physical drive
|
||||
/ number and only an FAT volume found on the physical drive will be mounted.
|
||||
/ When this function is enabled (1), each logical drive number can be bound to
|
||||
/ When this feature is enabled (1), each logical drive number can be bound to
|
||||
/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
|
||||
/ function will be available. */
|
||||
/ will be available. */
|
||||
|
||||
|
||||
#define FF_MIN_SS 512
|
||||
@ -198,25 +198,25 @@
|
||||
/* This set of options configures the range of sector size to be supported. (512,
|
||||
/ 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
|
||||
/ harddisk, but a larger value may be required for on-board flash memory and some
|
||||
/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured
|
||||
/ for variable sector size mode and disk_ioctl() function needs to implement
|
||||
/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is
|
||||
/ configured for variable sector size mode and disk_ioctl() needs to implement
|
||||
/ GET_SECTOR_SIZE command. */
|
||||
|
||||
|
||||
#define FF_LBA64 0
|
||||
#define FF_LBA64 1
|
||||
/* This option switches support for 64-bit LBA. (0:Disable or 1:Enable)
|
||||
/ To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */
|
||||
|
||||
|
||||
#define FF_MIN_GPT 0x10000000
|
||||
/* Minimum number of sectors to switch GPT as partitioning format in f_mkfs and
|
||||
/ f_fdisk function. 0x100000000 max. This option has no effect when FF_LBA64 == 0. */
|
||||
/* Minimum number of sectors to switch GPT as partitioning format in f_mkfs() and
|
||||
/ f_fdisk(). 2^32 sectors maximum. This option has no effect when FF_LBA64 == 0. */
|
||||
|
||||
|
||||
#define FF_USE_TRIM 0
|
||||
/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable)
|
||||
/ To enable Trim function, also CTRL_TRIM command should be implemented to the
|
||||
/ disk_ioctl() function. */
|
||||
/ To enable this feature, also CTRL_TRIM command should be implemented to
|
||||
/ the disk_ioctl(). */
|
||||
|
||||
|
||||
|
||||
@ -240,20 +240,20 @@
|
||||
#define FF_FS_NORTC 0
|
||||
#define FF_NORTC_MON 1
|
||||
#define FF_NORTC_MDAY 1
|
||||
#define FF_NORTC_YEAR 2022
|
||||
#define FF_NORTC_YEAR 2025
|
||||
/* The option FF_FS_NORTC switches timestamp feature. If the system does not have
|
||||
/ an RTC or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable the
|
||||
/ timestamp feature. Every object modified by FatFs will have a fixed timestamp
|
||||
/ defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.
|
||||
/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be
|
||||
/ added to the project to read current time form real-time clock. FF_NORTC_MON,
|
||||
/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() need to be added
|
||||
/ to the project to read current time form real-time clock. FF_NORTC_MON,
|
||||
/ FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.
|
||||
/ These options have no effect in read-only configuration (FF_FS_READONLY = 1). */
|
||||
|
||||
|
||||
#define FF_FS_NOFSINFO 0
|
||||
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
|
||||
/ option, and f_getfree() function at the first time after volume mount will force
|
||||
/ option, and f_getfree() at the first time after volume mount will force
|
||||
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
|
||||
/
|
||||
/ bit0=0: Use free cluster count in the FSINFO if available.
|
||||
@ -280,13 +280,13 @@
|
||||
/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
|
||||
/ module itself. Note that regardless of this option, file access to different
|
||||
/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
|
||||
/ and f_fdisk() function, are always not re-entrant. Only file/directory access
|
||||
/ to the same volume is under control of this featuer.
|
||||
/ and f_fdisk(), are always not re-entrant. Only file/directory access to
|
||||
/ the same volume is under control of this featuer.
|
||||
/
|
||||
/ 0: Disable re-entrancy. FF_FS_TIMEOUT have no effect.
|
||||
/ 1: Enable re-entrancy. Also user provided synchronization handlers,
|
||||
/ ff_mutex_create(), ff_mutex_delete(), ff_mutex_take() and ff_mutex_give()
|
||||
/ function, must be added to the project. Samples are available in ffsystem.c.
|
||||
/ ff_mutex_create(), ff_mutex_delete(), ff_mutex_take() and ff_mutex_give(),
|
||||
/ must be added to the project. Samples are available in ffsystem.c.
|
||||
/
|
||||
/ The FF_FS_TIMEOUT defines timeout period in unit of O/S time tick.
|
||||
*/
|
||||
|
@ -3,7 +3,7 @@
|
||||
#include "vr4300.h"
|
||||
|
||||
|
||||
static void cache_operation (uint8_t operation, uint8_t line_size, void *address, size_t length) {
|
||||
static inline void cache_operation (const uint8_t operation, uint8_t line_size, void *address, size_t length) {
|
||||
uint32_t cache_address = (((uint32_t) (address)) & (~(line_size - 1)));
|
||||
while (cache_address < ((uint32_t) (address) + length)) {
|
||||
asm volatile (
|
||||
@ -109,3 +109,27 @@ void pi_dma_write (io32_t *address, void *buffer, size_t length) {
|
||||
});
|
||||
while (pi_busy());
|
||||
}
|
||||
|
||||
uint32_t si_busy (void) {
|
||||
return (cpu_io_read(&SI->SR) & (SI_SR_IO_BUSY | SI_SR_DMA_BUSY));
|
||||
}
|
||||
|
||||
void si_dma_read (void *buffer) {
|
||||
cache_data_hit_writeback_invalidate(buffer, PIF_RAM_LENGTH);
|
||||
WITH_INTERRUPTS_DISABLED({
|
||||
while (si_busy());
|
||||
cpu_io_write(&SI->MADDR, (uint32_t) (PHYSICAL(buffer)));
|
||||
cpu_io_write(&SI->RDMA, (uint32_t) (PIF_RAM));
|
||||
});
|
||||
while (si_busy());
|
||||
}
|
||||
|
||||
void si_dma_write (void *buffer) {
|
||||
cache_data_hit_writeback(buffer, PIF_RAM_LENGTH);
|
||||
WITH_INTERRUPTS_DISABLED({
|
||||
while (si_busy());
|
||||
cpu_io_write(&SI->MADDR, (uint32_t) (PHYSICAL(buffer)));
|
||||
cpu_io_write(&SI->WDMA, (uint32_t) (PIF_RAM));
|
||||
});
|
||||
while (si_busy());
|
||||
}
|
||||
|
@ -202,6 +202,24 @@ typedef struct {
|
||||
#define PI_SR_CLR_INTR (1 << 1)
|
||||
|
||||
|
||||
typedef struct {
|
||||
io32_t MADDR;
|
||||
io32_t RDMA;
|
||||
io32_t __reserved_0[2];
|
||||
io32_t WDMA;
|
||||
io32_t __reserved_1[1];
|
||||
io32_t SR;
|
||||
} si_regs_t;
|
||||
|
||||
#define SI_BASE (0x04800000UL)
|
||||
#define SI ((si_regs_t *) SI_BASE)
|
||||
|
||||
#define SI_SR_DMA_BUSY (1 << 0)
|
||||
#define SI_SR_IO_BUSY (1 << 1)
|
||||
#define SI_SR_DMA_ERROR (1 << 3)
|
||||
#define SI_SR_INTERRUPT (1 << 12)
|
||||
|
||||
|
||||
#define ROM_DDIPL_BASE (0x06000000UL)
|
||||
#define ROM_DDIPL ((io32_t *) ROM_DDIPL_BASE)
|
||||
|
||||
@ -210,6 +228,14 @@ typedef struct {
|
||||
#define ROM_CART ((io32_t *) ROM_CART_BASE)
|
||||
|
||||
|
||||
#define PIF_RAM_BASE (0x1FC007C0)
|
||||
#define PIF_RAM_LENGTH (64)
|
||||
#define PIF_RAM ((io32_t *) PIF_RAM_BASE)
|
||||
|
||||
|
||||
void cache_data_hit_writeback_invalidate (void *address, size_t length);
|
||||
void cache_data_hit_writeback (void *address, size_t length);
|
||||
void cache_inst_hit_invalidate (void *address, size_t length);
|
||||
uint32_t c0_count (void);
|
||||
void delay_ms (int ms);
|
||||
uint32_t cpu_io_read (io32_t *address);
|
||||
@ -220,9 +246,9 @@ uint32_t pi_io_read (io32_t *address);
|
||||
void pi_io_write (io32_t *address, uint32_t value);
|
||||
void pi_dma_read (io32_t *address, void *buffer, size_t length);
|
||||
void pi_dma_write (io32_t *address, void *buffer, size_t length);
|
||||
void cache_data_hit_writeback_invalidate (void *address, size_t length);
|
||||
void cache_data_hit_writeback (void *address, size_t length);
|
||||
void cache_inst_hit_invalidate (void *address, size_t length);
|
||||
uint32_t si_busy (void);
|
||||
void si_dma_read (void *buffer);
|
||||
void si_dma_write (void *buffer);
|
||||
|
||||
|
||||
#endif
|
||||
|
169
sw/bootloader/src/joybus.c
Normal file
169
sw/bootloader/src/joybus.c
Normal file
@ -0,0 +1,169 @@
|
||||
#include <string.h>
|
||||
#include "io.h"
|
||||
#include "joybus.h"
|
||||
|
||||
|
||||
#define PIF_ESCAPE_SKIP_CHANNEL (0x00)
|
||||
#define PIF_ESCAPE_END_OF_FRAME (0xFE)
|
||||
|
||||
#define PIF_COMMAND_JOYBUS_START (1 << 0)
|
||||
|
||||
#define JOYBUS_CMD_INFO (0x00)
|
||||
#define JOYBUS_CMD_STATE (0x01)
|
||||
#define JOYBUS_CMD_RESET (0xFF)
|
||||
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
struct __attribute__((packed)) {
|
||||
uint8_t skip : 1;
|
||||
uint8_t reset : 1;
|
||||
uint8_t length : 6;
|
||||
} tx;
|
||||
struct __attribute__((packed)) {
|
||||
uint8_t no_device : 1;
|
||||
uint8_t timeout : 1;
|
||||
uint8_t length : 6;
|
||||
} rx;
|
||||
uint8_t cmd;
|
||||
} joybus_trx_info_t;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
joybus_trx_info_t info;
|
||||
struct __attribute__((packed)) {
|
||||
uint16_t id;
|
||||
uint8_t flags;
|
||||
} rx;
|
||||
} joybus_cmd_info_t;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
joybus_trx_info_t info;
|
||||
struct __attribute__((packed)) {
|
||||
uint8_t a : 1;
|
||||
uint8_t b : 1;
|
||||
uint8_t z : 1;
|
||||
uint8_t start : 1;
|
||||
uint8_t up : 1;
|
||||
uint8_t down : 1;
|
||||
uint8_t left : 1;
|
||||
uint8_t right : 1;
|
||||
uint8_t reset : 1;
|
||||
uint8_t __unused : 1;
|
||||
uint8_t l : 1;
|
||||
uint8_t r : 1;
|
||||
uint8_t c_up : 1;
|
||||
uint8_t c_down : 1;
|
||||
uint8_t c_left : 1;
|
||||
uint8_t c_right : 1;
|
||||
int8_t x;
|
||||
int8_t y;
|
||||
} rx;
|
||||
} joybus_cmd_state_t;
|
||||
|
||||
|
||||
static void joybus_clear_buffer (uint8_t *buffer) {
|
||||
for (int i = 0; i < PIF_RAM_LENGTH; i++) {
|
||||
buffer[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void joybus_set_starting_channel (uint8_t **ptr, int channel) {
|
||||
for (int i = 0; i < channel; i++) {
|
||||
**ptr = PIF_ESCAPE_SKIP_CHANNEL;
|
||||
*ptr += 1;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t *joybus_append_command (uint8_t **ptr, void *cmd, size_t length) {
|
||||
((joybus_trx_info_t *) (cmd))->tx.length += 1;
|
||||
uint8_t *cmd_ptr = *ptr;
|
||||
memcpy(cmd_ptr, cmd, length);
|
||||
*ptr += length;
|
||||
return cmd_ptr;
|
||||
}
|
||||
|
||||
static void joybus_load_result (uint8_t *cmd_ptr, void *cmd, size_t length) {
|
||||
memcpy(cmd, cmd_ptr, length);
|
||||
}
|
||||
|
||||
static void joybus_finish_frame (uint8_t **ptr) {
|
||||
**ptr = PIF_ESCAPE_END_OF_FRAME;
|
||||
*ptr += 1;
|
||||
}
|
||||
|
||||
static void joybus_perform_transaction (uint8_t *buffer) {
|
||||
buffer[PIF_RAM_LENGTH - 1] = PIF_COMMAND_JOYBUS_START;
|
||||
|
||||
si_dma_write(buffer);
|
||||
si_dma_read(buffer);
|
||||
}
|
||||
|
||||
static bool joybus_device_present (joybus_trx_info_t *trx_info) {
|
||||
return !(trx_info->rx.no_device || trx_info->rx.timeout);
|
||||
}
|
||||
|
||||
static bool joybus_execute_command (int port, void *cmd, size_t length) {
|
||||
uint8_t buffer[PIF_RAM_LENGTH] __attribute__((aligned(8)));
|
||||
uint8_t *ptr = buffer;
|
||||
|
||||
joybus_clear_buffer(buffer);
|
||||
joybus_set_starting_channel(&ptr, port);
|
||||
uint8_t *cmd_ptr = joybus_append_command(&ptr, cmd, length);
|
||||
joybus_finish_frame(&ptr);
|
||||
joybus_perform_transaction(buffer);
|
||||
joybus_load_result(cmd_ptr, cmd, length);
|
||||
return joybus_device_present((joybus_trx_info_t *) (cmd_ptr));
|
||||
}
|
||||
|
||||
static void joybus_copy_controller_info (joybus_cmd_info_t *cmd, joybus_controller_info_t *info) {
|
||||
info->id = cmd->rx.id;
|
||||
info->flags = cmd->rx.flags;
|
||||
}
|
||||
|
||||
static void joybus_copy_controller_state (joybus_cmd_state_t *cmd, joybus_controller_state_t *state) {
|
||||
state->a = cmd->rx.a;
|
||||
state->b = cmd->rx.b;
|
||||
state->z = cmd->rx.z;
|
||||
state->start = cmd->rx.start;
|
||||
state->up = cmd->rx.up;
|
||||
state->down = cmd->rx.down;
|
||||
state->left = cmd->rx.left;
|
||||
state->right = cmd->rx.right;
|
||||
state->reset = cmd->rx.reset;
|
||||
state->l = cmd->rx.l;
|
||||
state->r = cmd->rx.r;
|
||||
state->c_up = cmd->rx.c_up;
|
||||
state->c_down = cmd->rx.c_down;
|
||||
state->c_left = cmd->rx.c_left;
|
||||
state->c_right = cmd->rx.c_right;
|
||||
state->x = cmd->rx.x;
|
||||
state->y = cmd->rx.y;
|
||||
}
|
||||
|
||||
|
||||
bool joybus_get_controller_info (int port, joybus_controller_info_t *info, bool reset) {
|
||||
joybus_cmd_info_t cmd = { .info = {
|
||||
.cmd = reset ? JOYBUS_CMD_RESET : JOYBUS_CMD_INFO,
|
||||
.rx = { .length = sizeof(cmd.rx) }
|
||||
} };
|
||||
|
||||
if (joybus_execute_command(port, &cmd, sizeof(cmd))) {
|
||||
joybus_copy_controller_info(&cmd, info);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool joybus_get_controller_state (int port, joybus_controller_state_t *state) {
|
||||
joybus_cmd_state_t cmd = { .info = {
|
||||
.cmd = JOYBUS_CMD_STATE,
|
||||
.rx = { .length = sizeof(cmd.rx) }
|
||||
} };
|
||||
|
||||
if (joybus_execute_command(port, &cmd, sizeof(cmd))) {
|
||||
joybus_copy_controller_state(&cmd, state);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
39
sw/bootloader/src/joybus.h
Normal file
39
sw/bootloader/src/joybus.h
Normal file
@ -0,0 +1,39 @@
|
||||
#ifndef JOYBUS_H__
|
||||
#define JOYBUS_H__
|
||||
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint16_t id;
|
||||
uint8_t flags;
|
||||
} joybus_controller_info_t;
|
||||
|
||||
typedef struct {
|
||||
bool a;
|
||||
bool b;
|
||||
bool z;
|
||||
bool start;
|
||||
bool up;
|
||||
bool down;
|
||||
bool left;
|
||||
bool right;
|
||||
bool reset;
|
||||
bool l;
|
||||
bool r;
|
||||
bool c_up;
|
||||
bool c_down;
|
||||
bool c_left;
|
||||
bool c_right;
|
||||
int8_t x;
|
||||
int8_t y;
|
||||
} joybus_controller_state_t;
|
||||
|
||||
|
||||
bool joybus_get_controller_info (int port, joybus_controller_info_t *info, bool reset);
|
||||
bool joybus_get_controller_state (int port, joybus_controller_state_t *state);
|
||||
|
||||
|
||||
#endif
|
@ -3,6 +3,7 @@
|
||||
#include "error.h"
|
||||
#include "init.h"
|
||||
#include "io.h"
|
||||
#include "joybus.h"
|
||||
#include "menu.h"
|
||||
#include "sc64.h"
|
||||
|
||||
@ -22,6 +23,20 @@ void main (void) {
|
||||
boot_params.cic_seed = (sc64_boot_params.cic_seed & 0xFF);
|
||||
boot_params.detect_cic_seed = (sc64_boot_params.cic_seed == CIC_SEED_AUTO);
|
||||
|
||||
switch (sc64_boot_params.boot_mode) {
|
||||
case BOOT_MODE_ROM:
|
||||
case BOOT_MODE_DDIPL: {
|
||||
joybus_controller_state_t controller_state;
|
||||
if (joybus_get_controller_state(0, &controller_state) && controller_state.r) {
|
||||
sc64_boot_params.boot_mode = BOOT_MODE_MENU;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (sc64_boot_params.boot_mode) {
|
||||
case BOOT_MODE_MENU:
|
||||
menu_load();
|
||||
|
@ -15,49 +15,61 @@ static const char *fatfs_error_codes[] = {
|
||||
"No error",
|
||||
"A hard error occurred in the low level disk I/O layer",
|
||||
"Assertion failed",
|
||||
"The physical drive cannot work",
|
||||
"The physical drive does not work",
|
||||
"Could not find the file",
|
||||
"Could not find the path",
|
||||
"The path name format is invalid",
|
||||
"Access denied due to prohibited access or directory full",
|
||||
"Access denied due to prohibited access",
|
||||
"Access denied due to a prohibited access or directory full",
|
||||
"Access denied due to a prohibited access",
|
||||
"The file/directory object is invalid",
|
||||
"The physical drive is write protected",
|
||||
"The logical drive number is invalid",
|
||||
"The volume has no work area",
|
||||
"There is no valid FAT volume",
|
||||
"The f_mkfs() aborted due to any problem",
|
||||
"Could not get a grant to access the volume within defined period",
|
||||
"Could not find a valid FAT volume",
|
||||
"The f_mkfs function aborted due to some problem",
|
||||
"Could not take control of the volume within defined period",
|
||||
"The operation is rejected according to the file sharing policy",
|
||||
"LFN working buffer could not be allocated",
|
||||
"LFN working buffer could not be allocated or given buffer is insufficient in size",
|
||||
"Number of open files > FF_FS_LOCK",
|
||||
"Given parameter is invalid",
|
||||
};
|
||||
|
||||
|
||||
#define FF_CHECK(x, message, ...) { \
|
||||
fresult = x; \
|
||||
if (fresult != FR_OK) { \
|
||||
error_display( \
|
||||
message "\n" \
|
||||
" > FatFs error: %s\n" \
|
||||
" > SD card error: %s (%08X)", \
|
||||
__VA_ARGS__ __VA_OPT__(,) \
|
||||
fatfs_error_codes[fresult], \
|
||||
sc64_error_description(sc64_error_fatfs), sc64_error_fatfs \
|
||||
); \
|
||||
} \
|
||||
static void menu_fix_file_size (FIL *fil) {
|
||||
fil->obj.objsize = ALIGN(f_size(fil), FF_MAX_SS);
|
||||
}
|
||||
|
||||
static void menu_error_display (const char *message, FRESULT fresult) {
|
||||
error_display(
|
||||
" > FatFs error: %s\n"
|
||||
" > SD card error: %s (%08X)\n"
|
||||
"\n"
|
||||
"[ %s ]\n"
|
||||
" > Please insert a FAT32/exFAT formatted SD card.\n"
|
||||
" > To start the menu please put \"sc64menu.n64\" file\n"
|
||||
" in the top directory on the SD card.\n"
|
||||
" > Latest menu version is available on the\n"
|
||||
" https://menu.summercart64.dev website.\n",
|
||||
fatfs_error_codes[fresult],
|
||||
sc64_error_description(sc64_error_fatfs),
|
||||
sc64_error_fatfs,
|
||||
message
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
static void fix_menu_file_size (FIL *fil) {
|
||||
fil->obj.objsize = ALIGN(f_size(fil), FF_MAX_SS);
|
||||
#define FF_CHECK(x, message) { \
|
||||
fresult = x; \
|
||||
if (fresult != FR_OK) { \
|
||||
menu_error_display(message, fresult); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
void menu_load (void) {
|
||||
sc64_error_t error;
|
||||
bool writeback_pending;
|
||||
sc64_sd_card_status_t sd_card_status;
|
||||
FRESULT fresult;
|
||||
FATFS fs;
|
||||
FIL fil;
|
||||
@ -73,9 +85,17 @@ void menu_load (void) {
|
||||
error_display("Could not disable save writeback\n (%08X) - %s", error, sc64_error_description(error));
|
||||
}
|
||||
|
||||
if ((error = sc64_sd_card_get_status(&sd_card_status)) != SC64_OK) {
|
||||
error_display("Could not get SD card status\n (%08X) - %s", error, sc64_error_description(error));
|
||||
}
|
||||
|
||||
if (!(sd_card_status & SD_CARD_STATUS_INSERTED)) {
|
||||
menu_error_display("No SD card detected in the slot", FR_OK);
|
||||
}
|
||||
|
||||
FF_CHECK(f_mount(&fs, "", 1), "SD card initialize error");
|
||||
FF_CHECK(f_open(&fil, "sc64menu.n64", FA_READ), "Could not open menu executable (sc64menu.n64)");
|
||||
fix_menu_file_size(&fil);
|
||||
FF_CHECK(f_open(&fil, "sc64menu.n64", FA_READ), "Could not open menu executable");
|
||||
menu_fix_file_size(&fil);
|
||||
FF_CHECK(f_read(&fil, (void *) (ROM_ADDRESS), f_size(&fil), &bytes_read), "Could not read menu file");
|
||||
FF_CHECK((bytes_read != f_size(&fil)) ? FR_INT_ERR : FR_OK, "Read size is different than expected");
|
||||
FF_CHECK(f_close(&fil), "Could not close menu file");
|
||||
|
@ -104,7 +104,8 @@ typedef enum {
|
||||
SAVE_TYPE_SRAM = 3,
|
||||
SAVE_TYPE_FLASHRAM = 4,
|
||||
SAVE_TYPE_SRAM_BANKED = 5,
|
||||
SAVE_TYPE_SRAM_1M = 6
|
||||
SAVE_TYPE_SRAM_1M = 6,
|
||||
SAVE_TYPE_FLASHRAM_FAKE = 7,
|
||||
} sc64_save_type_t;
|
||||
|
||||
typedef enum {
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "fatfs/ff.h"
|
||||
#include "init.h"
|
||||
#include "io.h"
|
||||
#include "joybus.h"
|
||||
#include "sc64.h"
|
||||
#include "test.h"
|
||||
|
||||
@ -55,6 +56,7 @@ static void fill_random (uint32_t *buffer, int size, uint32_t pattern, uint32_t
|
||||
static void test_sc64_cfg (void) {
|
||||
sc64_error_t error;
|
||||
uint32_t button_state;
|
||||
joybus_controller_info_t controller_info;
|
||||
uint32_t identifier;
|
||||
uint16_t major;
|
||||
uint16_t minor;
|
||||
@ -71,6 +73,10 @@ static void test_sc64_cfg (void) {
|
||||
|
||||
display_printf("done\n\n");
|
||||
|
||||
if (joybus_get_controller_info(0, &controller_info, false)) {
|
||||
display_printf("Found controller on port 0: 0x%04X | 0x%02X\n\n", controller_info.id, controller_info.flags);
|
||||
}
|
||||
|
||||
if ((error = sc64_get_identifier(&identifier)) != SC64_OK) {
|
||||
error_display("Command IDENTIFIER_GET failed\n (%08X) - %s", error, sc64_error_description(error));
|
||||
}
|
||||
@ -178,7 +184,7 @@ static void test_sd_card_io (void) {
|
||||
uint8_t sector[512] __attribute__((aligned(8)));
|
||||
|
||||
if ((error = sc64_sd_card_get_status(&card_status)) != SC64_OK) {
|
||||
error_display("Could not get SD card info\n (%08X) - %s", error, sc64_error_description(error));
|
||||
error_display("Could not get SD card status\n (%08X) - %s", error, sc64_error_description(error));
|
||||
}
|
||||
|
||||
if (card_status & SD_CARD_STATUS_INSERTED) {
|
||||
@ -188,11 +194,11 @@ static void test_sd_card_io (void) {
|
||||
}
|
||||
|
||||
if ((error = sc64_sd_card_deinit()) != SC64_OK) {
|
||||
error_display("SD card deinit error\n (%08X) - %s", error, sc64_error_description(error));
|
||||
return display_printf("SD card deinit error, skipping test\n (%08X) - %s", error, sc64_error_description(error));
|
||||
}
|
||||
|
||||
if ((error = sc64_sd_card_init()) != SC64_OK) {
|
||||
return display_printf("SD card init error\n (%08X) - %s\n", error, sc64_error_description(error));
|
||||
return display_printf("SD card init error, skipping test\n (%08X) - %s\n", error, sc64_error_description(error));
|
||||
}
|
||||
|
||||
if ((error = sc64_sd_card_get_status(&card_status)) != SC64_OK) {
|
||||
@ -261,7 +267,7 @@ static void test_sd_card_fatfs (void) {
|
||||
UINT bytes;
|
||||
|
||||
if ((error = sc64_sd_card_deinit()) != SC64_OK) {
|
||||
error_display("SD card deinit error\n (%08X) - %s", error, sc64_error_description(error));
|
||||
return display_printf("SD card deinit error, skipping test\n (%08X) - %s", error, sc64_error_description(error));
|
||||
}
|
||||
|
||||
if ((fresult = f_mount(&fs, "", 1)) != FR_OK) {
|
||||
|
@ -35,9 +35,10 @@ const struct {
|
||||
|
||||
|
||||
void version_print (void) {
|
||||
display_printf("[ SC64 bootloader metadata ]\n");
|
||||
display_printf("branch: %s | tag: %s\n", version.git_branch, version.git_tag);
|
||||
display_printf("[ SC64 btldr ] %s | %s\n", version.git_branch, version.git_tag);
|
||||
display_printf("sha: %s\n", version.git_sha);
|
||||
display_printf("msg: %s\n", version.git_message);
|
||||
display_printf("\n");
|
||||
display_printf("Copyright 2020 - 2025 Mateusz Faderewski\n");
|
||||
display_printf("Licensed under | FW/SW: GPL-3.0 | HW: CERN-OHL-S-2.0\n");
|
||||
display_printf("Official website: https://summercart64.dev\n\n");
|
||||
}
|
||||
|
@ -92,15 +92,6 @@ typedef enum {
|
||||
TV_TYPE_PASSTHROUGH = 3
|
||||
} tv_type_t;
|
||||
|
||||
typedef enum {
|
||||
SD_CARD_OP_DEINIT = 0,
|
||||
SD_CARD_OP_INIT = 1,
|
||||
SD_CARD_OP_GET_STATUS = 2,
|
||||
SD_CARD_OP_GET_INFO = 3,
|
||||
SD_CARD_OP_BYTE_SWAP_ON = 4,
|
||||
SD_CARD_OP_BYTE_SWAP_OFF = 5,
|
||||
} sd_card_op_t;
|
||||
|
||||
typedef enum {
|
||||
DIAGNOSTIC_ID_VOLTAGE_TEMPERATURE = 0,
|
||||
} diagnostic_id_t;
|
||||
@ -287,6 +278,9 @@ static bool cfg_set_save_type (save_type_t save_type) {
|
||||
case SAVE_TYPE_SRAM_1M:
|
||||
cfg_change_scr_bits(CFG_SCR_SRAM_ENABLED, true);
|
||||
break;
|
||||
case SAVE_TYPE_FLASHRAM_FAKE:
|
||||
cfg_change_scr_bits(CFG_SCR_FLASHRAM_ENABLED, true);
|
||||
break;
|
||||
default:
|
||||
save_type = SAVE_TYPE_NONE;
|
||||
break;
|
||||
@ -641,15 +635,15 @@ void cfg_process (void) {
|
||||
case CMD_ID_SD_CARD_OP: {
|
||||
sd_error_t error = SD_OK;
|
||||
switch (p.data[1]) {
|
||||
case SD_CARD_OP_DEINIT:
|
||||
error = sd_get_lock(SD_LOCK_N64);
|
||||
case SD_OP_DEINIT:
|
||||
error = sd_try_lock(SD_LOCK_N64);
|
||||
if (error == SD_OK) {
|
||||
sd_card_deinit();
|
||||
sd_release_lock(SD_LOCK_N64);
|
||||
}
|
||||
break;
|
||||
|
||||
case SD_CARD_OP_INIT:
|
||||
case SD_OP_INIT:
|
||||
error = sd_try_lock(SD_LOCK_N64);
|
||||
if (error == SD_OK) {
|
||||
led_activity_on();
|
||||
@ -661,11 +655,11 @@ void cfg_process (void) {
|
||||
}
|
||||
break;
|
||||
|
||||
case SD_CARD_OP_GET_STATUS:
|
||||
case SD_OP_GET_STATUS:
|
||||
p.data[1] = sd_card_get_status();
|
||||
break;
|
||||
|
||||
case SD_CARD_OP_GET_INFO:
|
||||
case SD_OP_GET_INFO:
|
||||
if (cfg_translate_address(&p.data[0], SD_CARD_INFO_SIZE, (SDRAM | BRAM))) {
|
||||
return cfg_cmd_reply_error(ERROR_TYPE_SD_CARD, SD_ERROR_INVALID_ADDRESS);
|
||||
}
|
||||
@ -675,14 +669,14 @@ void cfg_process (void) {
|
||||
}
|
||||
break;
|
||||
|
||||
case SD_CARD_OP_BYTE_SWAP_ON:
|
||||
case SD_OP_BYTE_SWAP_ON:
|
||||
error = sd_get_lock(SD_LOCK_N64);
|
||||
if (error == SD_OK) {
|
||||
error = sd_set_byte_swap(true);
|
||||
}
|
||||
break;
|
||||
|
||||
case SD_CARD_OP_BYTE_SWAP_OFF:
|
||||
case SD_OP_BYTE_SWAP_OFF:
|
||||
error = sd_get_lock(SD_LOCK_N64);
|
||||
if (error == SD_OK) {
|
||||
error = sd_set_byte_swap(false);
|
||||
|
@ -14,6 +14,7 @@ typedef enum {
|
||||
SAVE_TYPE_FLASHRAM = 4,
|
||||
SAVE_TYPE_SRAM_BANKED = 5,
|
||||
SAVE_TYPE_SRAM_1M = 6,
|
||||
SAVE_TYPE_FLASHRAM_FAKE = 7,
|
||||
__SAVE_TYPE_COUNT
|
||||
} save_type_t;
|
||||
|
||||
|
@ -1,12 +1,18 @@
|
||||
#include <stdint.h>
|
||||
#include "cfg.h"
|
||||
#include "fpga.h"
|
||||
#include "hw.h"
|
||||
#include "timer.h"
|
||||
|
||||
|
||||
#define FLASHRAM_SIZE (128 * 1024)
|
||||
#define FLASHRAM_SECTOR_SIZE (16 * 1024)
|
||||
#define FLASHRAM_PAGE_SIZE (128)
|
||||
#define FLASHRAM_ADDRESS (0x03FE0000UL)
|
||||
#define FLASHRAM_BUFFER_ADDRESS (0x05002C00UL)
|
||||
#define FLASHRAM_SIZE (128 * 1024)
|
||||
#define FLASHRAM_SECTOR_SIZE (16 * 1024)
|
||||
#define FLASHRAM_PAGE_SIZE (128)
|
||||
#define FLASHRAM_ADDRESS (0x03FE0000UL)
|
||||
#define FLASHRAM_BUFFER_ADDRESS (0x05002C00UL)
|
||||
|
||||
#define FLASHRAM_WRITE_TIMING_MS (3)
|
||||
#define FLASHRAM_ERASE_TIMING_MS (200)
|
||||
|
||||
|
||||
typedef enum {
|
||||
@ -16,6 +22,13 @@ typedef enum {
|
||||
OP_WRITE_PAGE
|
||||
} flashram_op_t;
|
||||
|
||||
struct process {
|
||||
bool pending;
|
||||
};
|
||||
|
||||
|
||||
static struct process p;
|
||||
|
||||
|
||||
static flashram_op_t flashram_operation_type (uint32_t scr) {
|
||||
if (!(scr & FLASHRAM_SCR_PENDING)) {
|
||||
@ -38,6 +51,7 @@ void flashram_init (void) {
|
||||
if (fpga_reg_get(REG_FLASHRAM_SCR) & FLASHRAM_SCR_PENDING) {
|
||||
fpga_reg_set(REG_FLASHRAM_SCR, FLASHRAM_SCR_DONE);
|
||||
}
|
||||
p.pending = false;
|
||||
}
|
||||
|
||||
|
||||
@ -53,8 +67,15 @@ void flashram_process (void) {
|
||||
uint8_t write_buffer[FLASHRAM_PAGE_SIZE];
|
||||
|
||||
uint32_t page = ((scr & FLASHRAM_SCR_PAGE_MASK) >> FLASHRAM_SCR_PAGE_BIT);
|
||||
const bool full_emulation = (cfg_get_save_type() != SAVE_TYPE_FLASHRAM_FAKE);
|
||||
|
||||
if (op == OP_WRITE_PAGE) {
|
||||
if (p.pending) {
|
||||
if (timer_countdown_elapsed(TIMER_ID_FLASHRAM)) {
|
||||
p.pending = false;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else if (op == OP_WRITE_PAGE) {
|
||||
uint8_t page_buffer[FLASHRAM_PAGE_SIZE];
|
||||
|
||||
uint32_t address = (FLASHRAM_ADDRESS + (page * FLASHRAM_PAGE_SIZE));
|
||||
@ -63,11 +84,24 @@ void flashram_process (void) {
|
||||
fpga_mem_read(address, FLASHRAM_PAGE_SIZE, write_buffer);
|
||||
|
||||
for (int i = 0; i < FLASHRAM_PAGE_SIZE; i++) {
|
||||
write_buffer[i] &= page_buffer[i];
|
||||
if (full_emulation) {
|
||||
write_buffer[i] &= page_buffer[i];
|
||||
} else {
|
||||
write_buffer[i] = page_buffer[i];
|
||||
}
|
||||
}
|
||||
|
||||
fpga_mem_write(address, FLASHRAM_PAGE_SIZE, write_buffer);
|
||||
} else if ((op == OP_ERASE_SECTOR) || (op == OP_ERASE_ALL)) {
|
||||
|
||||
if (full_emulation) {
|
||||
hw_delay_ms(FLASHRAM_WRITE_TIMING_MS);
|
||||
}
|
||||
} else if ((op == OP_ERASE_SECTOR) || (op == OP_ERASE_ALL)) {
|
||||
if (full_emulation) {
|
||||
p.pending = true;
|
||||
timer_countdown_start(TIMER_ID_FLASHRAM, FLASHRAM_ERASE_TIMING_MS);
|
||||
}
|
||||
|
||||
for (int i = 0; i < FLASHRAM_PAGE_SIZE; i++) {
|
||||
write_buffer[i] = 0xFF;
|
||||
}
|
||||
@ -80,6 +114,10 @@ void flashram_process (void) {
|
||||
for (uint32_t offset = 0; offset < erase_size; offset += FLASHRAM_PAGE_SIZE) {
|
||||
fpga_mem_write(address + offset, FLASHRAM_PAGE_SIZE, write_buffer);
|
||||
}
|
||||
|
||||
if (full_emulation) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fpga_reg_set(REG_FLASHRAM_SCR, FLASHRAM_SCR_DONE);
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include "timer.h"
|
||||
|
||||
|
||||
#define SD_INIT_BUFFER_ADDRESS (0x05002BB8UL)
|
||||
#define SD_INIT_BUFFER_ADDRESS (0x05002A00UL)
|
||||
#define BYTE_SWAP_ADDRESS_END (0x05000000UL)
|
||||
|
||||
#define CMD6_ARG_CHECK_HS (0x00FFFFF1UL)
|
||||
@ -35,11 +35,10 @@
|
||||
#define R7_CHECK_PATTERN (0xAA << 0)
|
||||
|
||||
#define TIMEOUT_INIT_MS (1000)
|
||||
#define TIMEOUT_DATA_MS (5000)
|
||||
|
||||
#define DAT_CRC16_LENGTH (8)
|
||||
#define DAT_BLOCK_MAX_COUNT (256)
|
||||
#define DAT_TIMEOUT_INIT_MS (2000)
|
||||
#define DAT_TIMEOUT_DATA_MS (5000)
|
||||
|
||||
|
||||
typedef enum {
|
||||
@ -59,16 +58,12 @@ typedef enum {
|
||||
RSP_R7,
|
||||
} rsp_type_t;
|
||||
|
||||
typedef enum {
|
||||
DAT_READ,
|
||||
DAT_WRITE,
|
||||
} dat_mode_t;
|
||||
|
||||
typedef enum {
|
||||
DAT_OK,
|
||||
DAT_BUSY,
|
||||
DAT_ERROR_IO,
|
||||
DAT_ERROR_TIMEOUT,
|
||||
} dat_error_t;
|
||||
} dat_status_t;
|
||||
|
||||
typedef enum {
|
||||
CMD6_OK,
|
||||
@ -111,6 +106,7 @@ static void sd_set_clock (sd_clock_t mode) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static bool sd_cmd (uint8_t cmd, uint32_t arg, rsp_type_t rsp_type, void *rsp) {
|
||||
uint32_t scr;
|
||||
uint32_t cmd_data;
|
||||
@ -176,52 +172,109 @@ static bool sd_acmd (uint8_t acmd, uint32_t arg, rsp_type_t rsp_type, void *rsp)
|
||||
return false;
|
||||
}
|
||||
|
||||
static void sd_dat_prepare (uint32_t address, uint32_t count, dat_mode_t mode) {
|
||||
uint32_t length = (count * SD_SECTOR_SIZE);
|
||||
uint32_t sd_dat = (((count - 1) << SD_DAT_BLOCKS_BIT) | SD_DAT_FIFO_FLUSH);
|
||||
uint32_t sd_dma_scr = DMA_SCR_START;
|
||||
|
||||
if (mode == DAT_READ) {
|
||||
sd_dat |= SD_DAT_START_READ;
|
||||
sd_dma_scr |= DMA_SCR_DIRECTION;
|
||||
if (p.byte_swap && (address < BYTE_SWAP_ADDRESS_END)) {
|
||||
sd_dma_scr |= DMA_SCR_BYTE_SWAP;
|
||||
}
|
||||
} else {
|
||||
sd_dat |= SD_DAT_START_WRITE;
|
||||
static void sd_dat_start_write (uint32_t count) {
|
||||
uint32_t dat = (((count - 1) << SD_DAT_BLOCKS_BIT) | SD_DAT_START_WRITE | SD_DAT_FIFO_FLUSH);
|
||||
fpga_reg_set(REG_SD_DAT, dat);
|
||||
}
|
||||
|
||||
static void sd_dat_start_read (uint32_t count) {
|
||||
uint32_t dat = (((count - 1) << SD_DAT_BLOCKS_BIT) | SD_DAT_START_READ | SD_DAT_FIFO_FLUSH);
|
||||
fpga_reg_set(REG_SD_DAT, dat);
|
||||
}
|
||||
|
||||
static dat_status_t sd_dat_status (void) {
|
||||
uint32_t dat = fpga_reg_get(REG_SD_DAT);
|
||||
if (dat & SD_DAT_BUSY) {
|
||||
return DAT_BUSY;
|
||||
}
|
||||
|
||||
fpga_reg_set(REG_SD_DAT, sd_dat);
|
||||
fpga_reg_set(REG_SD_DMA_ADDRESS, address);
|
||||
fpga_reg_set(REG_SD_DMA_LENGTH, length);
|
||||
fpga_reg_set(REG_SD_DMA_SCR, sd_dma_scr);
|
||||
if (dat & SD_DAT_ERROR) {
|
||||
return DAT_ERROR_IO;
|
||||
}
|
||||
return DAT_OK;
|
||||
}
|
||||
|
||||
static void sd_dat_abort (void) {
|
||||
fpga_reg_set(REG_SD_DMA_SCR, DMA_SCR_STOP);
|
||||
fpga_reg_set(REG_SD_DAT, SD_DAT_STOP | SD_DAT_FIFO_FLUSH);
|
||||
}
|
||||
|
||||
static dat_error_t sd_dat_wait (uint16_t timeout_ms) {
|
||||
|
||||
static void sd_dma_start_write (uint32_t address, uint32_t count) {
|
||||
uint32_t length = (count * SD_SECTOR_SIZE);
|
||||
uint32_t scr = DMA_SCR_START;
|
||||
|
||||
fpga_reg_set(REG_SD_DMA_ADDRESS, address);
|
||||
fpga_reg_set(REG_SD_DMA_LENGTH, length);
|
||||
fpga_reg_set(REG_SD_DMA_SCR, scr);
|
||||
}
|
||||
|
||||
static void sd_dma_start_read (uint32_t address, uint32_t count) {
|
||||
uint32_t length = (count * SD_SECTOR_SIZE);
|
||||
uint32_t scr = (DMA_SCR_DIRECTION | DMA_SCR_START);
|
||||
|
||||
if (p.byte_swap && (address < BYTE_SWAP_ADDRESS_END)) {
|
||||
scr |= DMA_SCR_BYTE_SWAP;
|
||||
}
|
||||
|
||||
fpga_reg_set(REG_SD_DMA_ADDRESS, address);
|
||||
fpga_reg_set(REG_SD_DMA_LENGTH, length);
|
||||
fpga_reg_set(REG_SD_DMA_SCR, scr);
|
||||
}
|
||||
|
||||
static bool sd_dma_is_busy (void) {
|
||||
return (fpga_reg_get(REG_SD_DMA_SCR) & DMA_SCR_BUSY);
|
||||
}
|
||||
|
||||
static void sd_dma_abort (void) {
|
||||
fpga_reg_set(REG_SD_DMA_SCR, DMA_SCR_STOP);
|
||||
while (sd_dma_is_busy());
|
||||
}
|
||||
|
||||
|
||||
static void sd_start_write (uint32_t address, uint32_t count) {
|
||||
sd_dat_start_write(count);
|
||||
sd_dma_start_write(address, count);
|
||||
}
|
||||
|
||||
static void sd_start_read (uint32_t address, uint32_t count) {
|
||||
sd_dat_start_read(count);
|
||||
sd_dma_start_read(address, count);
|
||||
}
|
||||
|
||||
static void sd_abort (void) {
|
||||
sd_dma_abort();
|
||||
sd_dat_abort();
|
||||
}
|
||||
|
||||
static dat_status_t sd_sync (uint16_t timeout_ms) {
|
||||
timer_countdown_start(TIMER_ID_SD, timeout_ms);
|
||||
|
||||
do {
|
||||
uint32_t sd_dat = fpga_reg_get(REG_SD_DAT);
|
||||
uint32_t sd_dma_scr = fpga_reg_get(REG_SD_DMA_SCR);
|
||||
if ((!(sd_dat & SD_DAT_BUSY)) && (!(sd_dma_scr & DMA_SCR_BUSY))) {
|
||||
if (sd_dat & SD_DAT_ERROR) {
|
||||
sd_dat_abort();
|
||||
return DAT_ERROR_IO;
|
||||
}
|
||||
return DAT_OK;
|
||||
while (true) {
|
||||
dat_status_t status = sd_dat_status();
|
||||
|
||||
switch (status) {
|
||||
case DAT_BUSY:
|
||||
break;
|
||||
|
||||
case DAT_OK:
|
||||
if (!sd_dma_is_busy()) {
|
||||
return DAT_OK;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
sd_abort();
|
||||
return status;
|
||||
}
|
||||
} while (!timer_countdown_elapsed(TIMER_ID_SD));
|
||||
|
||||
sd_dat_abort();
|
||||
|
||||
return DAT_ERROR_TIMEOUT;
|
||||
if (timer_countdown_elapsed(TIMER_ID_SD)) {
|
||||
sd_abort();
|
||||
return DAT_ERROR_TIMEOUT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static bool sd_dat_check_crc16 (uint8_t *data, uint32_t length) {
|
||||
uint16_t device_crc[4];
|
||||
uint16_t controller_crc[4];
|
||||
@ -266,15 +319,15 @@ static bool sd_dat_check_crc16 (uint8_t *data, uint32_t length) {
|
||||
|
||||
static cmd6_error_t sd_cmd6 (uint32_t arg, uint8_t *buffer) {
|
||||
uint32_t rsp;
|
||||
sd_dat_prepare(SD_INIT_BUFFER_ADDRESS, 1, DAT_READ);
|
||||
sd_start_read(SD_INIT_BUFFER_ADDRESS, 1);
|
||||
if (sd_cmd(6, arg, RSP_R1, NULL)) {
|
||||
sd_dat_abort();
|
||||
sd_abort();
|
||||
if ((!sd_cmd(13, p.rca, RSP_R1, &rsp)) && (rsp & R1_ILLEGAL_COMMAND)) {
|
||||
return CMD6_ERROR_ILLEGAL_CMD;
|
||||
}
|
||||
return CMD6_ERROR_IO;
|
||||
}
|
||||
if (sd_dat_wait(DAT_TIMEOUT_INIT_MS) == DAT_ERROR_TIMEOUT) {
|
||||
if (sd_sync(TIMEOUT_DATA_MS) == DAT_ERROR_TIMEOUT) {
|
||||
return CMD6_ERROR_TIMEOUT;
|
||||
}
|
||||
fpga_mem_read(SD_INIT_BUFFER_ADDRESS, CMD6_DATA_LENGTH + DAT_CRC16_LENGTH, buffer);
|
||||
@ -495,13 +548,12 @@ sd_error_t sd_write_sectors (uint32_t address, uint32_t sector, uint32_t count)
|
||||
if (sd_cmd(25, sector, RSP_R1, NULL)) {
|
||||
return SD_ERROR_CMD25_IO;
|
||||
}
|
||||
sd_dat_prepare(address, blocks, DAT_WRITE);
|
||||
dat_error_t error = sd_dat_wait(DAT_TIMEOUT_DATA_MS);
|
||||
if (error != DAT_OK) {
|
||||
sd_cmd(12, 0, RSP_R1b, NULL);
|
||||
return (error == DAT_ERROR_IO) ? SD_ERROR_CMD25_CRC : SD_ERROR_CMD25_TIMEOUT;
|
||||
}
|
||||
sd_start_write(address, blocks);
|
||||
dat_status_t status = sd_sync(TIMEOUT_DATA_MS);
|
||||
sd_cmd(12, 0, RSP_R1b, NULL);
|
||||
if (status != DAT_OK) {
|
||||
return (status == DAT_ERROR_IO) ? SD_ERROR_CMD25_CRC : SD_ERROR_CMD25_TIMEOUT;
|
||||
}
|
||||
address += (blocks * SD_SECTOR_SIZE);
|
||||
sector += (blocks * (p.card_type_block ? 1 : SD_SECTOR_SIZE));
|
||||
count -= blocks;
|
||||
@ -529,17 +581,16 @@ sd_error_t sd_read_sectors (uint32_t address, uint32_t sector, uint32_t count) {
|
||||
|
||||
while (count > 0) {
|
||||
uint32_t blocks = ((count > DAT_BLOCK_MAX_COUNT) ? DAT_BLOCK_MAX_COUNT : count);
|
||||
sd_dat_prepare(address, blocks, DAT_READ);
|
||||
sd_start_read(address, blocks);
|
||||
if (sd_cmd(18, sector, RSP_R1, NULL)) {
|
||||
sd_dat_abort();
|
||||
sd_abort();
|
||||
return SD_ERROR_CMD18_IO;
|
||||
}
|
||||
dat_error_t error = sd_dat_wait(DAT_TIMEOUT_DATA_MS);
|
||||
if (error != DAT_OK) {
|
||||
sd_cmd(12, 0, RSP_R1b, NULL);
|
||||
return (error == DAT_ERROR_IO) ? SD_ERROR_CMD18_CRC : SD_ERROR_CMD18_TIMEOUT;
|
||||
}
|
||||
dat_status_t status = sd_sync(TIMEOUT_DATA_MS);
|
||||
sd_cmd(12, 0, RSP_R1b, NULL);
|
||||
if (status != DAT_OK) {
|
||||
return (status == DAT_ERROR_IO) ? SD_ERROR_CMD18_CRC : SD_ERROR_CMD18_TIMEOUT;
|
||||
}
|
||||
address += (blocks * SD_SECTOR_SIZE);
|
||||
sector += (blocks * (p.card_type_block ? 1 : SD_SECTOR_SIZE));
|
||||
count -= blocks;
|
||||
@ -577,6 +628,7 @@ sd_error_t sd_optimize_sectors (uint32_t address, uint32_t *sector_table, uint32
|
||||
return SD_OK;
|
||||
}
|
||||
|
||||
|
||||
sd_error_t sd_get_lock (sd_lock_t lock) {
|
||||
if (p.lock == lock) {
|
||||
return SD_OK;
|
||||
|
@ -52,6 +52,15 @@ typedef enum {
|
||||
SD_LOCK_USB,
|
||||
} sd_lock_t;
|
||||
|
||||
typedef enum {
|
||||
SD_OP_DEINIT = 0,
|
||||
SD_OP_INIT = 1,
|
||||
SD_OP_GET_STATUS = 2,
|
||||
SD_OP_GET_INFO = 3,
|
||||
SD_OP_BYTE_SWAP_ON = 4,
|
||||
SD_OP_BYTE_SWAP_OFF = 5,
|
||||
} sd_op_t;
|
||||
|
||||
|
||||
sd_error_t sd_card_init (void);
|
||||
void sd_card_deinit (void);
|
||||
|
@ -13,6 +13,7 @@ typedef enum {
|
||||
TIMER_ID_SD,
|
||||
TIMER_ID_USB,
|
||||
TIMER_ID_WRITEBACK,
|
||||
TIMER_ID_FLASHRAM,
|
||||
__TIMER_ID_COUNT
|
||||
} timer_id_t;
|
||||
|
||||
|
@ -390,15 +390,15 @@ static void usb_rx_process (void) {
|
||||
case 'i': {
|
||||
sd_error_t error = SD_OK;
|
||||
switch (p.rx_args[1]) {
|
||||
case 0:
|
||||
error = sd_get_lock(SD_LOCK_USB);
|
||||
case SD_OP_DEINIT:
|
||||
error = sd_try_lock(SD_LOCK_USB);
|
||||
if (error == SD_OK) {
|
||||
sd_card_deinit();
|
||||
sd_release_lock(SD_LOCK_USB);
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case SD_OP_INIT:
|
||||
error = sd_try_lock(SD_LOCK_USB);
|
||||
if (error == SD_OK) {
|
||||
led_activity_on();
|
||||
@ -410,10 +410,10 @@ static void usb_rx_process (void) {
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
case SD_OP_GET_STATUS:
|
||||
break;
|
||||
|
||||
case 3:
|
||||
case SD_OP_GET_INFO:
|
||||
if (usb_validate_address_length(p.rx_args[0], SD_CARD_INFO_SIZE, true)) {
|
||||
error = SD_ERROR_INVALID_ADDRESS;
|
||||
} else {
|
||||
@ -424,14 +424,14 @@ static void usb_rx_process (void) {
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
case SD_OP_BYTE_SWAP_ON:
|
||||
error = sd_get_lock(SD_LOCK_USB);
|
||||
if (error == SD_OK) {
|
||||
error = sd_set_byte_swap(true);
|
||||
}
|
||||
break;
|
||||
|
||||
case 5:
|
||||
case SD_OP_BYTE_SWAP_OFF:
|
||||
error = sd_get_lock(SD_LOCK_USB);
|
||||
if (error == SD_OK) {
|
||||
error = sd_set_byte_swap(false);
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#define VERSION_MAJOR (2)
|
||||
#define VERSION_MINOR (20)
|
||||
#define VERSION_REVISION (0)
|
||||
#define VERSION_REVISION (2)
|
||||
|
||||
|
||||
void version_firmware (uint32_t *version, uint32_t *revision) {
|
||||
|
@ -62,6 +62,10 @@ static save_type_t writeback_get_address_length (uint32_t *address, uint32_t *le
|
||||
*address = SRAM_FLASHRAM_ADDRESS;
|
||||
*length = SRAM_1M_LENGTH;
|
||||
break;
|
||||
case SAVE_TYPE_FLASHRAM_FAKE:
|
||||
*address = SRAM_FLASHRAM_ADDRESS;
|
||||
*length = FLASHRAM_LENGTH;
|
||||
break;
|
||||
default:
|
||||
*address = 0;
|
||||
*length = 0;
|
||||
|
2
sw/deployer/Cargo.lock
generated
2
sw/deployer/Cargo.lock
generated
@ -1293,7 +1293,7 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "sc64deployer"
|
||||
version = "2.20.0"
|
||||
version = "2.20.2"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"cc",
|
||||
|
@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "sc64deployer"
|
||||
version = "2.20.0"
|
||||
version = "2.20.2"
|
||||
edition = "2021"
|
||||
authors = ["Polprzewodnikowy"]
|
||||
authors = ["Mateusz Faderewski (Polprzewodnikowy)"]
|
||||
description = "SummerCart64 loader and control software"
|
||||
documentation = "https://github.com/Polprzewodnikowy/SummerCart64"
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("cargo::rerun-if-changed=../bootloader/src/");
|
||||
|
||||
let out_dir = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap());
|
||||
|
||||
cc::Build::new()
|
||||
|
@ -21,7 +21,36 @@ use std::{
|
||||
};
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
#[command(
|
||||
author,
|
||||
version,
|
||||
about,
|
||||
help_template = "\
|
||||
{before-help}{name} v{version} - {about}
|
||||
Copyright (C) 2020 - 2025 {author}
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
Latest information about SummerCart64 is available
|
||||
on the official website: <https://summercart64.dev/>.
|
||||
|
||||
|
||||
{usage-heading} {usage}
|
||||
|
||||
{all-args}{after-help}"
|
||||
)]
|
||||
struct Cli {
|
||||
#[command(subcommand)]
|
||||
command: Commands,
|
||||
@ -330,6 +359,7 @@ enum SaveType {
|
||||
SramBanked,
|
||||
Sram1m,
|
||||
Flashram,
|
||||
FlashramFake,
|
||||
}
|
||||
|
||||
impl From<n64::SaveType> for SaveType {
|
||||
@ -356,6 +386,7 @@ impl From<SaveType> for sc64::SaveType {
|
||||
SaveType::SramBanked => Self::SramBanked,
|
||||
SaveType::Sram1m => Self::Sram1m,
|
||||
SaveType::Flashram => Self::Flashram,
|
||||
SaveType::FlashramFake => Self::FlashramFake,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -499,7 +530,16 @@ fn handle_upload_command(connection: Connection, args: &UploadArgs) -> Result<()
|
||||
sc64.set_tv_type(tv_type)?;
|
||||
}
|
||||
|
||||
sc64.calculate_cic_parameters(args.cic_seed)?;
|
||||
let (seed, checksum, matched) = sc64.calculate_cic_parameters(args.cic_seed)?;
|
||||
if !matched {
|
||||
println!(
|
||||
"{}",
|
||||
"Warning: IPL3 in the ROM does not match any known variant. It may fail to boot."
|
||||
.bright_yellow(),
|
||||
);
|
||||
println!("IPL3 has been automatically signed with the parameters listed below:");
|
||||
println!("[seed = 0x{seed:02X} | checksum = 0x{checksum:012X}]");
|
||||
}
|
||||
|
||||
if args.reboot && !sc64.try_notify_via_aux(sc64::AuxMessage::Reboot)? {
|
||||
println!(
|
||||
@ -614,7 +654,16 @@ fn handle_64dd_command(connection: Connection, args: &_64DDArgs) -> Result<(), s
|
||||
sc64.set_tv_type(tv_type)?;
|
||||
}
|
||||
|
||||
sc64.calculate_cic_parameters(args.cic_seed)?;
|
||||
let (seed, checksum, matched) = sc64.calculate_cic_parameters(args.cic_seed)?;
|
||||
if !matched {
|
||||
println!(
|
||||
"{}",
|
||||
"Warning: IPL3 in the ROM does not match any known variant. It may fail to boot."
|
||||
.bright_yellow(),
|
||||
);
|
||||
println!("IPL3 has been automatically signed with the parameters listed below:");
|
||||
println!("[seed = 0x{seed:02X} | checksum = 0x{checksum:012X}]");
|
||||
}
|
||||
|
||||
if args.disk.len() == 0 {
|
||||
let dd_mode = sc64::DdMode::DdIpl;
|
||||
|
@ -110,35 +110,35 @@ fn calculate_ipl3_checksum(ipl3: &[u8], seed: u8) -> Result<u64, Error> {
|
||||
Ok(checksum)
|
||||
}
|
||||
|
||||
pub fn sign_ipl3(ipl3: &[u8], custom_seed: Option<u8>) -> Result<(u8, u64), Error> {
|
||||
pub fn sign_ipl3(ipl3: &[u8], custom_seed: Option<u8>) -> Result<(u8, u64, bool), Error> {
|
||||
if let Some(seed) = custom_seed {
|
||||
Ok((seed, calculate_ipl3_checksum(ipl3, seed)?))
|
||||
} else {
|
||||
let known_seed_checksum_pairs = [
|
||||
(0xDD, 0x083C6C77E0B1u64), // 5167
|
||||
(0x3F, 0x45CC73EE317Au64), // 6101
|
||||
(0x3F, 0x44160EC5D9AFu64), // 7102
|
||||
(0x3F, 0xA536C0F1D859u64), // 6102/7102
|
||||
(0x78, 0x586FD4709867u64), // 6103/7103
|
||||
(0x91, 0x8618A45BC2D3u64), // 6105/7105
|
||||
(0x85, 0x2BBAD4E6EB74u64), // 6106/7106
|
||||
(0xDD, 0x6EE8D9E84970u64), // NDXJ0
|
||||
(0xDD, 0x6C216495C8B9u64), // NDDJ0
|
||||
(0xDD, 0xE27F43BA93ACu64), // NDDJ1
|
||||
(0xDD, 0x32B294E2AB90u64), // NDDJ2
|
||||
(0xDE, 0x05BA2EF0A5F1u64), // NDDE0
|
||||
];
|
||||
|
||||
for (seed, checksum) in known_seed_checksum_pairs {
|
||||
if calculate_ipl3_checksum(ipl3, seed)? == checksum {
|
||||
return Ok((seed, checksum));
|
||||
}
|
||||
}
|
||||
|
||||
// Unknown IPL3 detected, sign it with arbitrary seed (CIC6102/7101 value is used here)
|
||||
const DEFAULT_SEED: u8 = 0x3F;
|
||||
let checksum = calculate_ipl3_checksum(ipl3, DEFAULT_SEED)?;
|
||||
|
||||
Ok((DEFAULT_SEED, checksum))
|
||||
return Ok((seed, calculate_ipl3_checksum(ipl3, seed)?, true));
|
||||
}
|
||||
|
||||
let known_seed_checksum_pairs = [
|
||||
(0xDD, 0x083C6C77E0B1u64), // 5167
|
||||
(0x3F, 0x45CC73EE317Au64), // 6101
|
||||
(0x3F, 0x44160EC5D9AFu64), // 7102
|
||||
(0x3F, 0xA536C0F1D859u64), // 6102/7102
|
||||
(0x78, 0x586FD4709867u64), // 6103/7103
|
||||
(0x91, 0x8618A45BC2D3u64), // 6105/7105
|
||||
(0x85, 0x2BBAD4E6EB74u64), // 6106/7106
|
||||
(0xDD, 0x6EE8D9E84970u64), // NDXJ0
|
||||
(0xDD, 0x6C216495C8B9u64), // NDDJ0
|
||||
(0xDD, 0xE27F43BA93ACu64), // NDDJ1
|
||||
(0xDD, 0x32B294E2AB90u64), // NDDJ2
|
||||
(0xDE, 0x05BA2EF0A5F1u64), // NDDE0
|
||||
];
|
||||
|
||||
for (seed, checksum) in known_seed_checksum_pairs {
|
||||
if calculate_ipl3_checksum(ipl3, seed)? == checksum {
|
||||
return Ok((seed, checksum, true));
|
||||
}
|
||||
}
|
||||
|
||||
// Unknown IPL3 detected, sign it with arbitrary seed (CIC6102/7101 value is used here)
|
||||
const DEFAULT_SEED: u8 = 0x3F;
|
||||
let checksum = calculate_ipl3_checksum(ipl3, DEFAULT_SEED)?;
|
||||
|
||||
Ok((DEFAULT_SEED, checksum, false))
|
||||
}
|
||||
|
@ -57,21 +57,21 @@ mod fatfs {
|
||||
f.write_str(match self {
|
||||
Self::DiskErr => "A hard error occurred in the low level disk I/O layer",
|
||||
Self::IntErr => "Assertion failed",
|
||||
Self::NotReady => "The physical drive cannot work",
|
||||
Self::NotReady => "The physical drive does not work",
|
||||
Self::NoFile => "Could not find the file",
|
||||
Self::NoPath => "Could not find the path",
|
||||
Self::InvalidName => "The path name format is invalid",
|
||||
Self::Denied => "Access denied due to prohibited access or directory full",
|
||||
Self::Exist => "Access denied due to prohibited access",
|
||||
Self::Denied => "Access denied due to a prohibited access or directory full",
|
||||
Self::Exist => "Access denied due to a prohibited access",
|
||||
Self::InvalidObject => "The file/directory object is invalid",
|
||||
Self::WriteProtected => "The physical drive is write protected",
|
||||
Self::InvalidDrive => "The logical drive number is invalid",
|
||||
Self::NotEnabled => "The volume has no work area",
|
||||
Self::NoFilesystem => "There is no valid FAT volume",
|
||||
Self::MkfsAborted => "The f_mkfs() aborted due to any problem",
|
||||
Self::Timeout => "Could not get a grant to access the volume within defined period",
|
||||
Self::NoFilesystem => "Could not find a valid FAT volume",
|
||||
Self::MkfsAborted => "The f_mkfs function aborted due to some problem",
|
||||
Self::Timeout => "Could not take control of the volume within defined period",
|
||||
Self::Locked => "The operation is rejected according to the file sharing policy",
|
||||
Self::NotEnoughCore => "LFN working buffer could not be allocated",
|
||||
Self::NotEnoughCore => "LFN working buffer could not be allocated or given buffer is insufficient in size",
|
||||
Self::TooManyOpenFiles => "Number of open files > FF_FS_LOCK",
|
||||
Self::InvalidParameter => "Given parameter is invalid",
|
||||
Self::DriverInstalled => "FatFs driver is already installed",
|
||||
@ -290,14 +290,20 @@ impl FFDriver for SC64 {
|
||||
}
|
||||
|
||||
fn read(&mut self, buffer: &mut [u8], sector: fatfs::LBA_t) -> fatfs::DRESULT {
|
||||
if let Ok(SdCardResult::OK) = self.read_sd_card(buffer, sector) {
|
||||
if (sector + ((buffer.len() / SD_CARD_SECTOR_SIZE) as fatfs::LBA_t)) > 0x1_0000_0000 {
|
||||
return fatfs::DRESULT_RES_PARERR;
|
||||
}
|
||||
if let Ok(SdCardResult::OK) = self.read_sd_card(buffer, sector as u32) {
|
||||
return fatfs::DRESULT_RES_OK;
|
||||
}
|
||||
fatfs::DRESULT_RES_ERROR
|
||||
}
|
||||
|
||||
fn write(&mut self, buffer: &[u8], sector: fatfs::LBA_t) -> fatfs::DRESULT {
|
||||
if let Ok(SdCardResult::OK) = self.write_sd_card(buffer, sector) {
|
||||
if (sector + ((buffer.len() / SD_CARD_SECTOR_SIZE) as fatfs::LBA_t)) > 0x1_0000_0000 {
|
||||
return fatfs::DRESULT_RES_PARERR;
|
||||
}
|
||||
if let Ok(SdCardResult::OK) = self.write_sd_card(buffer, sector as u32) {
|
||||
return fatfs::DRESULT_RES_OK;
|
||||
}
|
||||
fatfs::DRESULT_RES_ERROR
|
||||
|
@ -73,8 +73,8 @@ impl Wrapper {
|
||||
let result = if devices > 0 {
|
||||
let mut list: Vec<DeviceInfo> = vec![];
|
||||
|
||||
let mut description = [0i8; 128];
|
||||
let mut serial = [0i8; 128];
|
||||
let mut description = [std::ffi::c_char::from(0); 128];
|
||||
let mut serial = [std::ffi::c_char::from(0); 128];
|
||||
|
||||
let mut device = device_list;
|
||||
let mut index = 0;
|
||||
|
@ -445,6 +445,7 @@ impl SC64 {
|
||||
SaveType::Flashram => (SAVE_ADDRESS, FLASHRAM_LENGTH),
|
||||
SaveType::SramBanked => (SAVE_ADDRESS, SRAM_BANKED_LENGTH),
|
||||
SaveType::Sram1m => (SAVE_ADDRESS, SRAM_1M_LENGTH),
|
||||
SaveType::FlashramFake => (SAVE_ADDRESS, FLASHRAM_LENGTH),
|
||||
};
|
||||
|
||||
if length != save_length {
|
||||
@ -469,6 +470,7 @@ impl SC64 {
|
||||
SaveType::Flashram => (SAVE_ADDRESS, FLASHRAM_LENGTH),
|
||||
SaveType::SramBanked => (SAVE_ADDRESS, SRAM_BANKED_LENGTH),
|
||||
SaveType::Sram1m => (SAVE_ADDRESS, SRAM_1M_LENGTH),
|
||||
SaveType::FlashramFake => (SAVE_ADDRESS, FLASHRAM_LENGTH),
|
||||
};
|
||||
|
||||
self.memory_read_chunked(writer, address, save_length)
|
||||
@ -486,7 +488,10 @@ impl SC64 {
|
||||
self.memory_read_chunked(writer, address, length)
|
||||
}
|
||||
|
||||
pub fn calculate_cic_parameters(&mut self, custom_seed: Option<u8>) -> Result<(), Error> {
|
||||
pub fn calculate_cic_parameters(
|
||||
&mut self,
|
||||
custom_seed: Option<u8>,
|
||||
) -> Result<(u8, u64, bool), Error> {
|
||||
let (address, cic_seed, boot_seed) = match get_config!(self, BootMode)? {
|
||||
BootMode::Menu => (BOOTLOADER_ADDRESS, None, None),
|
||||
BootMode::Rom => (BOOTLOADER_ADDRESS, None, custom_seed),
|
||||
@ -495,12 +500,12 @@ impl SC64 {
|
||||
BootMode::DirectDdIpl => (DDIPL_ADDRESS, custom_seed, None),
|
||||
};
|
||||
let ipl3 = self.command_memory_read(address + IPL3_OFFSET, IPL3_LENGTH)?;
|
||||
let (seed, checksum) = sign_ipl3(&ipl3, cic_seed)?;
|
||||
let (seed, checksum, matched) = sign_ipl3(&ipl3, cic_seed)?;
|
||||
self.command_cic_params_set(false, seed, checksum)?;
|
||||
if let Some(seed) = boot_seed {
|
||||
self.command_config_set(Config::CicSeed(CicSeed::Seed(seed)))?;
|
||||
}
|
||||
Ok(())
|
||||
Ok((seed, checksum, matched))
|
||||
}
|
||||
|
||||
pub fn set_boot_mode(&mut self, boot_mode: BootMode) -> Result<(), Error> {
|
||||
|
@ -288,6 +288,7 @@ pub enum SaveType {
|
||||
Flashram,
|
||||
SramBanked,
|
||||
Sram1m,
|
||||
FlashramFake,
|
||||
}
|
||||
|
||||
impl Display for SaveType {
|
||||
@ -300,6 +301,7 @@ impl Display for SaveType {
|
||||
Self::SramBanked => "SRAM 768k",
|
||||
Self::Flashram => "FlashRAM 1M",
|
||||
Self::Sram1m => "SRAM 1M",
|
||||
Self::FlashramFake => "FakeFlashRAM 1M"
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -315,6 +317,7 @@ impl TryFrom<u32> for SaveType {
|
||||
4 => Self::Flashram,
|
||||
5 => Self::SramBanked,
|
||||
6 => Self::Sram1m,
|
||||
7 => Self::FlashramFake,
|
||||
_ => return Err(Error::new("Unknown save type code")),
|
||||
})
|
||||
}
|
||||
@ -330,6 +333,7 @@ impl From<SaveType> for u32 {
|
||||
SaveType::Flashram => 4,
|
||||
SaveType::SramBanked => 5,
|
||||
SaveType::Sram1m => 6,
|
||||
SaveType::FlashramFake => 7,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,7 @@
|
||||
<menu class="mobile-hidden">
|
||||
<li><a href="/">Home</a></li>
|
||||
<li><a href="/features.html">Features</a></li>
|
||||
<li><a href="/faq.html">FAQ</a></li>
|
||||
<li><a href="https://menu.summercart64.dev">Menu</a></li>
|
||||
<li><a href="https://github.com/Polprzewodnikowy/SummerCart64/releases/latest">Downloads</a></li>
|
||||
<li class="active"><a href="/bom.html">Bill of materials</a></li>
|
||||
@ -39,7 +40,7 @@
|
||||
<iframe class="bom" src="/sc64v2_bom.html"></iframe>
|
||||
|
||||
<footer>
|
||||
<span>© 2020 - 2024 <a href="https://mateuszfaderewski.pl">Mateusz Faderewski</a></span>
|
||||
<span>© 2020 - 2025 <a href="https://mateuszfaderewski.pl">Mateusz Faderewski</a></span>
|
||||
</footer>
|
||||
</body>
|
||||
|
||||
|
247
web/faq.html
Normal file
247
web/faq.html
Normal file
@ -0,0 +1,247 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>SummerCart64 - Features</title>
|
||||
<meta property="og:title" content="SummerCart64 - FAQ" />
|
||||
<meta property="og:description" content="SummerCart64 - a fully open source N64 flashcart" />
|
||||
<meta property="og:url" content="https://summercart64.dev/faq.html" />
|
||||
<meta property="og:image" content="https://summercart64.dev/sc64-embed.png" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="icon" href="favicon.svg" sizes="any" type="image/svg+xml">
|
||||
<link rel="stylesheet" href="./styles.css">
|
||||
<script src="./script.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="menu-container">
|
||||
<div class="menu-bar">
|
||||
<div class="menu-buttons">
|
||||
<a href="/"><img src="sc64.svg"></a>
|
||||
<div class="menu-button" onclick="showMenu(event)">
|
||||
<div class="menu-button-line"></div>
|
||||
<div class="menu-button-line"></div>
|
||||
<div class="menu-button-line"></div>
|
||||
</div>
|
||||
</div>
|
||||
<menu class="mobile-hidden">
|
||||
<li><a href="/">Home</a></li>
|
||||
<li><a href="/features.html">Features</a></li>
|
||||
<li class="active"><a href="/faq.html">FAQ</a></li>
|
||||
<li><a href="https://menu.summercart64.dev">Menu</a></li>
|
||||
<li><a href="https://github.com/Polprzewodnikowy/SummerCart64/releases/latest">Downloads</a></li>
|
||||
<li><a href="/bom.html">Bill of materials</a></li>
|
||||
<li><a href="https://github.com/Polprzewodnikowy/SummerCart64">GitHub</a></li>
|
||||
</menu>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="main-container">
|
||||
<main>
|
||||
<h1>Frequently Asked Questions</h1>
|
||||
<section>
|
||||
<details>
|
||||
<summary>What is the SummerCart64?</summary>
|
||||
<div>
|
||||
<p>SummerCart64 is a device that lets you develop and test games on the N64 console. It is a
|
||||
similar device to the <a href="https://64drive.retroactive.be/">64drive</a>, or <a
|
||||
href="https://krikzz.com/our-products/cartridges/ed64x7.html">EverDrive-64 X7</a>. Its
|
||||
main strengths are fast I/O, developer oriented features, full 64DD emulation and being
|
||||
fully open source.</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Is this flashcart for game developers only, or for anyone?</summary>
|
||||
<div>
|
||||
<p>SummerCart64 started its life as a device to aid game development and testing on a real N64
|
||||
console. Over time scope of the project has expanded and currently it's a great choice for
|
||||
casual gamers and people interested in tinkering with the N64.</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Where can I get SummerCart64?</summary>
|
||||
<div>
|
||||
<p>For latest information check the <strong>Where to buy</strong> section on the <a
|
||||
href="/">home page</a>.</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Which SD cards are supported? Should I format the card in a specific way?</summary>
|
||||
<div>
|
||||
<p>SummerCart64 currently supports cards up to 2 TB in size. Supported filesystems are FAT32 and
|
||||
exFAT. Card can be formatted with any tool but it's recommended to use builtin tools
|
||||
available in your system - for Windows OS just right click on the SD card drive and select
|
||||
<strong>Format</strong> option. For other OS refer to the system's included help.
|
||||
</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>I don't care about developer features and just want to play games. How do I setup the SD
|
||||
card?</summary>
|
||||
<div>
|
||||
<p>All necessary information about installing menu and preparing SD card is available on the <a
|
||||
href="https://menu.summercart64.dev">N64FlashcartMenu official website</a>.</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>SummerCart64 won't boot and it blinks once every second. What's happening?</summary>
|
||||
<div>
|
||||
<p>Due to technical reasons N64 region lockout can be bypassed only after detecting if the
|
||||
current boot failed. To fix this just power off the console, and then power it on again. If
|
||||
the issue persist then check the next question.</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Why is there a battery on the board? Do I need it?</summary>
|
||||
<div>
|
||||
<p>Yes, battery is required for full operation. Battery keeps the time and persistent settings.
|
||||
This includes last detected console region. If your console region is PAL and SummerCart64
|
||||
doesn't boot while LED is blinking once every second then try inserting a battery into the
|
||||
holder.</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Which battery model do I need?</summary>
|
||||
<div>
|
||||
<p>Just a standard CR2032 button cell battery.</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Do I need an Expansion Pak?</summary>
|
||||
<div>
|
||||
<p>It depends. A common misconception is that the flashcart is able to expand N64 memory. This
|
||||
is technically not possible. The SummerCart64 on its own do not require an Expansion Pak to
|
||||
work. However, for some retail games the Expansion Pak is mandatory to even run the game.
|
||||
Cheat support and most ROM hacks are also dependent on the Expansion Pak.</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>I've tried to play a ROM hack / homebrew and it doesn't work. Why?</summary>
|
||||
<div>
|
||||
<p>Most of the ROM hacks available for the N64 absolutely <strong>require</strong> an Expansion
|
||||
Pak to be installed in the console. If the screen stays black after loading the game then
|
||||
either you don't have an Expansion Pak, or the ROM hack is not console compatible. Always
|
||||
verify if the game you're trying to run is designed to run on real hardware, and not just in
|
||||
an emulator.</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Can SummerCart64 replace a Controller Pak / Transfer Pak / Rumble Pak?</summary>
|
||||
<div>
|
||||
<p>No. SummerCart64 (and any other thing that is plugged in the cartridge slot) can't replace a
|
||||
device that plugs in the bottom slot of the controller.</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Do I need to press the reset button to save the game progress?</summary>
|
||||
<div>
|
||||
<p>No. SummerCart64 watches the console activity and automatically writes save data to the SD
|
||||
card <strong>while you play the game</strong>. Automatic writeback happens about 1 second
|
||||
after game finishes saving the progress.</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Where do I get games to play?</summary>
|
||||
<div>
|
||||
<p>You can check entries from the <a href="https://n64brew.dev/wiki/Category:Game_Jams">N64brew
|
||||
game jams</a>. There are plenty of gems to try out!</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>How can I update firmware?</summary>
|
||||
<div>
|
||||
<p>Instructions are available in the <a
|
||||
href="https://github.com/Polprzewodnikowy/SummerCart64/blob/main/docs/00_quick_startup_guide.md#firmware-backupupdate">official
|
||||
documentation</a>.
|
||||
</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><code>sc64deployer</code> can't find my cart, why?</summary>
|
||||
<div>
|
||||
<p>On Windows OS please check if you have latest FTDI drivers installed, either through Windows
|
||||
Update or directly from <a href="https://ftdichip.com/drivers/vcp-drivers">chip
|
||||
manufacturer's website</a>. On Linux OS check if you have proper permissions to the USB
|
||||
device.</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Can I use software other than <code>sc64deployer</code>?</summary>
|
||||
<div>
|
||||
<p>Yes. <a href="https://github.com/buu342/N64-UNFLoader">UNFLoader</a> is also supported.</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Real time clock is not keeping the time, what's wrong?</summary>
|
||||
<div>
|
||||
<p>Make sure that the battery is installed in the correct orientation. Don't forget to peel all
|
||||
protective plastic stickers from the battery before installation.</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>What is the purpose for button on the back?</summary>
|
||||
<div>
|
||||
<p>It has several uses:</p>
|
||||
<ol>
|
||||
<li>To eject/swap 64DD disks - when there are multiple virtual 64DD disks "inserted".</li>
|
||||
<li>To run the built-in test ROM - hold the button while switching the power on.</li>
|
||||
<li>For game developers as additional input.</li>
|
||||
</ol>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Can I access files on the microSD card via USB port on the PC?</summary>
|
||||
<div>
|
||||
<p>Yes. <code>sc64deployer</code> has a <code>sd</code> command to access files on the SD card.
|
||||
</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Does SummerCart64 come with a warranty?</summary>
|
||||
<div>
|
||||
<p>As a DIY product and an open-source project, The SummerCart64 (design/software and any other
|
||||
asset contained as part of this project) have no warranty or liability whatsoever. For any
|
||||
product that is distributed from an online or retail store, please seek warranty and/or help
|
||||
(as required by your country law) from them, not from the developers of the SummerCart64
|
||||
project.</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>I bought SummerCart64 from XXX store. It doesn't work or there are weird problems. What
|
||||
should I do?</summary>
|
||||
<div>
|
||||
<p>Always contact the seller you've bought the device to resolve the issues. Developers and
|
||||
community are not responsible for low quality hardware and lack of quality control.</p>
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<span>© 2020 - 2025 <a href="https://mateuszfaderewski.pl">Mateusz Faderewski</a></span>
|
||||
</footer>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -28,6 +28,7 @@
|
||||
<menu class="mobile-hidden">
|
||||
<li><a href="/">Home</a></li>
|
||||
<li class="active"><a href="/features.html">Features</a></li>
|
||||
<li><a href="/faq.html">FAQ</a></li>
|
||||
<li><a href="https://menu.summercart64.dev">Menu</a></li>
|
||||
<li><a href="https://github.com/Polprzewodnikowy/SummerCart64/releases/latest">Downloads</a></li>
|
||||
<li><a href="/bom.html">Bill of materials</a></li>
|
||||
@ -181,7 +182,7 @@
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<span>© 2020 - 2024 <a href="https://mateuszfaderewski.pl">Mateusz Faderewski</a></span>
|
||||
<span>© 2020 - 2025 <a href="https://mateuszfaderewski.pl">Mateusz Faderewski</a></span>
|
||||
</footer>
|
||||
</body>
|
||||
|
||||
|
@ -28,6 +28,7 @@
|
||||
<menu class="mobile-hidden">
|
||||
<li class="active"><a href="/">Home</a></li>
|
||||
<li><a href="/features.html">Features</a></li>
|
||||
<li><a href="/faq.html">FAQ</a></li>
|
||||
<li><a href="https://menu.summercart64.dev">Menu</a></li>
|
||||
<li><a href="https://github.com/Polprzewodnikowy/SummerCart64/releases/latest">Downloads</a></li>
|
||||
<li><a href="/bom.html">Bill of materials</a></li>
|
||||
@ -38,12 +39,12 @@
|
||||
|
||||
<div class="main-container">
|
||||
<main>
|
||||
<h1>What is SummerCart64?</h1>
|
||||
<h1>What is the SummerCart64?</h1>
|
||||
<section>
|
||||
<img class="sc64-logo" src="sc64.svg">
|
||||
<p>SummerCart64 is a custom made cartridge (commonly referred as a flashcart) that allows you to develop
|
||||
and play games on the N64 console. It is the first N64 flashcart that is feature complete and
|
||||
completely open source.</p>
|
||||
and play games on the N64 console. It is the first N64 flashcart that is feature complete and, at
|
||||
the same time, completely open source.</p>
|
||||
<p>With fast I/O, integrated support in <a href="https://libdragon.dev">libdragon</a> / <a
|
||||
href="https://github.com/devwizard64/libcart">libcart</a> / <a
|
||||
href="https://github.com/buu342/N64-UNFLoader">UNFLoader</a>, and well documented API, testing
|
||||
@ -71,7 +72,9 @@
|
||||
check the <code><strong>#sc64-forum</strong></code> channel.</li>
|
||||
<li><a href="https://www.pcbway.com/project/member/shareproject/?bmbno=1046ED64-8AEE-44">PCBWay
|
||||
Shared Project</a> page - both assembled boards and bare PCBs are available.</li>
|
||||
<li>Manual PCB and components order - DIY friendly option.</li>
|
||||
<li>Manual PCB and components order - DIY friendly option. For more details check out the <a
|
||||
href="https://github.com/Polprzewodnikowy/SummerCart64/blob/main/docs/06_build_guide.md">build
|
||||
guide</a>.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
@ -94,13 +97,10 @@
|
||||
|
||||
<h1>Support the project</h1>
|
||||
<section>
|
||||
<p>Like the project? Consider supporting it on the <a
|
||||
href="https://github.com/sponsors/Polprzewodnikowy">GitHub Sponsors</a>, <a
|
||||
href="https://ko-fi.com/polprzewodnikowy">Ko-fi</a>, or click the buttons below. Not necessary
|
||||
but greatly appreciated.</p>
|
||||
<p>Like the project? Consider supporting the effort on <a
|
||||
href="https://ko-fi.com/polprzewodnikowy">Ko-fi</a>, or click a button below. Not necessary
|
||||
but greatly appreciated. Tips are non-refundable, support responsibly.</p>
|
||||
<div class="sponsor-buttons">
|
||||
<iframe src="https://github.com/sponsors/Polprzewodnikowy/button" title="Sponsor Polprzewodnikowy"
|
||||
height="32" width="114" style="border: 0; border-radius: 6px;"></iframe>
|
||||
<a href='https://ko-fi.com/R5R13I07C' target='_blank'><img height='32'
|
||||
src='https://storage.ko-fi.com/cdn/kofi2.png?v=3' alt='Buy Me a Coffee at ko-fi.com' /></a>
|
||||
</div>
|
||||
@ -109,7 +109,7 @@
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<span>© 2020 - 2024 <a href="https://mateuszfaderewski.pl">Mateusz Faderewski</a></span>
|
||||
<span>© 2020 - 2025 <a href="https://mateuszfaderewski.pl">Mateusz Faderewski</a></span>
|
||||
</footer>
|
||||
</body>
|
||||
|
||||
|
@ -23,6 +23,8 @@
|
||||
|
||||
--sponsor-buttons-height: 32px;
|
||||
|
||||
--details-bg-color: rgb(56, 56, 128);
|
||||
|
||||
--footer-padding: 16px 32px 16px 32px;
|
||||
--footer-font-size: 14px;
|
||||
}
|
||||
@ -57,6 +59,7 @@ a:visited {
|
||||
a:active,
|
||||
a:hover {
|
||||
color: var(--text-color);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.menu-container {
|
||||
@ -259,6 +262,22 @@ main .separator {
|
||||
margin: var(--main-separator-margin) 0;
|
||||
}
|
||||
|
||||
details {
|
||||
margin: var(--main-paragraph-margin) 0;
|
||||
}
|
||||
|
||||
details summary {
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
details > div {
|
||||
overflow: auto;
|
||||
margin-top: var(--main-paragraph-margin);
|
||||
padding: 0 var(--main-paragraph-margin);
|
||||
background-color: var(--details-bg-color);
|
||||
}
|
||||
|
||||
iframe.bom {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
|
Loading…
x
Reference in New Issue
Block a user