├── .github └── workflows │ └── build-release-on-tag.yml ├── .gitignore ├── CHANGELOG.md ├── CMakeLists.txt ├── LICENSE ├── README.md ├── boards ├── murmulator.h └── murmulator2.h ├── drivers ├── audio │ ├── CMakeLists.txt │ ├── audio.c │ ├── audio.h │ └── audio_i2s.pio ├── fatfs │ ├── 00history.txt │ ├── 00readme.txt │ ├── CMakeLists.txt │ ├── diskio.h │ ├── f_util.c │ ├── f_util.h │ ├── fatfs.cmake │ ├── ff.c │ ├── ff.h │ ├── ffconf.h │ ├── ffsystem.c │ └── ffunicode.c ├── graphics │ ├── CMakeLists.txt │ ├── font6x8.h │ ├── font8x16.h │ ├── font8x8.h │ ├── graphics.c │ └── graphics.h ├── hdmi │ ├── CMakeLists.txt │ ├── hdmi.c │ └── hdmi.h ├── nespad │ ├── CMakeLists.txt │ ├── nespad.cpp │ └── nespad.h ├── ps2 │ ├── CMakeLists.txt │ ├── ps2 (2).c │ ├── ps2.c │ └── ps2.h ├── ps2kbd │ ├── CMakeLists.txt │ ├── hid.h │ ├── ps2kbd_mrmltr.cpp │ ├── ps2kbd_mrmltr.h │ └── ps2kbd_mrmltr.pio ├── sdcard │ ├── CMakeLists.txt │ ├── LICENSE │ ├── README.md │ ├── pio_spi.c │ ├── pio_spi.h │ ├── sdcard.c │ ├── sdcard.cmake │ ├── sdcard.h │ └── spi.pio ├── st7789 │ ├── CMakeLists.txt │ ├── st7789.c │ ├── st7789.h │ └── st7789.pio ├── tv-software │ ├── CMakeLists.txt │ ├── tv-software.c │ └── tv-software.h ├── tv │ ├── CMakeLists.txt │ ├── tv.c │ └── tv.h ├── usb │ ├── CMakeLists.txt │ ├── msc_disk.c │ ├── tusb_config.h │ ├── usb.c │ ├── usb.h │ └── usb_descriptors.c ├── usbfs │ ├── CMakeLists.txt │ ├── README.md │ ├── diskio.c │ ├── diskio.h │ ├── storage.c │ ├── tusb_config.h │ ├── usb.c │ ├── usb_descriptors.c │ ├── usbfs.c │ └── usbfs.h ├── vga-nextgen │ ├── CMakeLists.txt │ ├── vga.c │ └── vga.h └── ws2812 │ ├── CMakeLists.txt │ ├── ws2812.c │ ├── ws2812.h │ └── ws2812.pio ├── memmap_genesis.ld ├── pico_sdk_import.cmake └── src ├── gwenesis ├── bus │ ├── gwenesis_bus.c │ └── gwenesis_bus.h ├── cpus │ ├── M68K │ │ ├── m68k.h │ │ ├── m68kconf.h │ │ ├── m68kcpu.c │ │ ├── m68kcpu.h │ │ ├── m68ki_cycles.h │ │ ├── m68ki_cycles_full.h │ │ ├── m68ki_instruction_jump_table.h │ │ ├── m68ki_instruction_jump_table_full.h │ │ ├── m68kops.h │ │ ├── macros.h │ │ └── readme.txt │ └── Z80 │ │ ├── Codes.h │ │ ├── CodesCB.h │ │ ├── CodesED.h │ │ ├── CodesXCB.h │ │ ├── CodesXX.h │ │ ├── ConDebug.c │ │ ├── Debug.c │ │ ├── Tables.h │ │ ├── Z80.c │ │ └── Z80.h ├── io │ ├── gwenesis_io.c │ └── gwenesis_io.h ├── savestate │ ├── gwenesis_savestate.c │ └── gwenesis_savestate.h ├── sound │ ├── fn_table.h │ ├── gwenesis_sn76489.c │ ├── gwenesis_sn76489.h │ ├── lfo_pm_table.h │ ├── sin_tab.h │ ├── tl_tab.h │ ├── ym2612.c │ ├── ym2612.h │ ├── ym2612_OPN_ST_dt_tab.h │ ├── z80inst.c │ └── z80inst.h └── vdp │ ├── gwenesis_vdp.h │ ├── gwenesis_vdp_gfx.c │ └── gwenesis_vdp_mem.c ├── main.cpp └── tusb_config.h /.github/workflows/build-release-on-tag.yml: -------------------------------------------------------------------------------- 1 | name: Build and create a release when tag is pushed 2 | 3 | # Only deploy when a new tag is pushed 4 | on: 5 | push: 6 | tags: 7 | - "v*.*-alpha" 8 | - "v*.*.*" 9 | # branches: [ master ] 10 | # pull_request: 11 | # branches: [ master ] 12 | 13 | # Must match the project() name in CMakeLists.txt 14 | env: 15 | APP_NAME: genesis 16 | 17 | # Allow this workflow to write back to the repository 18 | permissions: 19 | contents: write 20 | 21 | # Build binary and send to releases 22 | jobs: 23 | build-release: 24 | runs-on: ubuntu-latest 25 | name: Build and release 26 | steps: 27 | 28 | - name: Install dependencies 29 | run: | 30 | sudo apt update && \ 31 | sudo apt install -y git python3 && \ 32 | sudo apt install -y cmake gcc-arm-none-eabi libnewlib-arm-none-eabi build-essential libusb-1.0-0-dev 33 | 34 | - name: Check out this repository 35 | uses: actions/checkout@v3 36 | 37 | - name: Print Working directory 38 | run: echo $HOME && pwd && ls -la 39 | 40 | - name: Update line containing pico_set_program_version() in CMakelists.txt with tag name. 41 | run: | 42 | # Extract the tag name that triggered the event and remove the 'refs/tags/' prefix 43 | input_string=${{ github.ref }} 44 | prefix="refs/tags/" 45 | tag="No versioninfo found" 46 | if [[ $input_string == $prefix* ]]; then 47 | echo "The string starts with 'refs/tags/'." 48 | tag="${input_string#$prefix}" 49 | echo "Tag is ${tag}" 50 | sed -i "s/^[[:space:]]*pico_set_program_version(.*/pico_set_program_version(${{ env.APP_NAME }} \"$tag\")/" CMakeLists.txt 51 | else 52 | echo "The string does not start with 'refs/tags/'." 53 | fi 54 | grep "pico_set_program_version" CMakeLists.txt 55 | 56 | - name: Install Pico SDk 57 | run: | 58 | cd $HOME && \ 59 | git clone --depth 1 https://github.com/raspberrypi/pico-sdk.git --branch master && \ 60 | cd pico-sdk/ && \ 61 | git submodule update --init 62 | 63 | 64 | 65 | - name: Build the basic version 66 | run: | 67 | export PICO_SDK_PATH=$HOME/pico-sdk && \ 68 | mkdir build && cd build && \ 69 | cmake .. && make 70 | 71 | - name: Build the overclocked versions 72 | run: | 73 | cd build && \ 74 | cmake -DTFT=Off -DILI9341=Off -DHDMI=Off -DTV=Off .. && make && \ 75 | cmake -DTFT=Off -DILI9341=Off -DHDMI=On -DTV=Off .. && make && \ 76 | cmake -DTFT=Off -DILI9341=Off -DHDMI=Off -DTV=On .. && make && \ 77 | cmake -DTFT=On -DILI9341=Off -DHDMI=Off -DTV=Off .. && make && \ 78 | cmake -DTFT=On -DILI9341=On -DHDMI=Off -DTV=Off .. && make && 79 | 80 | 81 | - name: Create release 82 | uses: softprops/action-gh-release@v1 83 | if: startsWith(github.ref, 'refs/tags/') 84 | with: 85 | files: | 86 | bin/Release/**.uf2 87 | 88 | body_path: CHANGELOG.md 89 | 90 | 91 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | /assets/ 3 | /bin/ 4 | build 5 | uf2 6 | cmake-* 7 | fh.log 8 | /pimoroni-pico/ 9 | build 10 | # Prerequisites 11 | *.d 12 | 13 | # Compiled Object files 14 | *.slo 15 | *.lo 16 | *.o 17 | *.obj 18 | 19 | # Precompiled Headers 20 | *.gch 21 | *.pch 22 | 23 | # Compiled Dynamic libraries 24 | *.so 25 | *.dylib 26 | *.dll 27 | 28 | # Fortran module files 29 | *.mod 30 | *.smod 31 | 32 | # Compiled Static libraries 33 | *.lai 34 | *.la 35 | *.a 36 | *.lib 37 | 38 | # Executables 39 | *.exe 40 | *.out 41 | *.app 42 | 43 | **/build 44 | .vscode 45 | 46 | # Apple filesystem cruft 47 | .DS_Store 48 | venv 49 | # Visual Studio stuff 50 | ## Ignore Visual Studio temporary files, build results, and 51 | ## files generated by popular Visual Studio add-ons. 52 | ## 53 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 54 | 55 | # User-specific files 56 | *.rsuser 57 | *.suo 58 | *.user 59 | *.userosscache 60 | *.sln.docstates 61 | 62 | # User-specific files (MonoDevelop/Xamarin Studio) 63 | *.userprefs 64 | 65 | # Mono auto generated files 66 | mono_crash.* 67 | 68 | # Build results 69 | [Dd]ebug/ 70 | [Dd]ebugPublic/ 71 | [Rr]elease/ 72 | [Rr]eleases/ 73 | x64/ 74 | x86/ 75 | [Aa][Rr][Mm]/ 76 | [Aa][Rr][Mm]64/ 77 | bld/ 78 | [Bb]in/ 79 | [Oo]bj/ 80 | [Ll]og/ 81 | [Ll]ogs/ 82 | [Bb]uild/ 83 | 84 | # Visual Studio 2015/2017 cache/options directory 85 | .vs/ 86 | # Uncomment if you have tasks that create the project's static files in wwwroot 87 | #wwwroot/ 88 | 89 | # Visual Studio 2017 auto generated files 90 | Generated\ Files/ 91 | 92 | # MSTest test Results 93 | [Tt]est[Rr]esult*/ 94 | [Bb]uild[Ll]og.* 95 | 96 | # NUnit 97 | *.VisualState.xml 98 | TestResult.xml 99 | nunit-*.xml 100 | 101 | # Build Results of an ATL Project 102 | [Dd]ebugPS/ 103 | [Rr]eleasePS/ 104 | dlldata.c 105 | 106 | # Benchmark Results 107 | BenchmarkDotNet.Artifacts/ 108 | 109 | # .NET Core 110 | project.lock.json 111 | project.fragment.lock.json 112 | artifacts/ 113 | 114 | # StyleCop 115 | StyleCopReport.xml 116 | 117 | # Files built by Visual Studio 118 | *_i.c 119 | *_p.c 120 | *_h.h 121 | *.ilk 122 | *.meta 123 | *.obj 124 | *.iobj 125 | *.pch 126 | *.pdb 127 | *.ipdb 128 | *.pgc 129 | *.pgd 130 | *.rsp 131 | *.sbr 132 | *.tlb 133 | *.tli 134 | *.tlh 135 | *.tmp 136 | *.tmp_proj 137 | *_wpftmp.csproj 138 | *.log 139 | *.vspscc 140 | *.vssscc 141 | .builds 142 | *.pidb 143 | *.svclog 144 | *.scc 145 | 146 | # Chutzpah Test files 147 | _Chutzpah* 148 | 149 | # Visual C++ cache files 150 | ipch/ 151 | *.aps 152 | *.ncb 153 | *.opendb 154 | *.opensdf 155 | *.sdf 156 | *.cachefile 157 | *.VC.db 158 | *.VC.VC.opendb 159 | 160 | # Visual Studio profiler 161 | *.psess 162 | *.vsp 163 | *.vspx 164 | *.sap 165 | 166 | # Visual Studio Trace Files 167 | *.e2e 168 | 169 | # TFS 2012 Local Workspace 170 | $tf/ 171 | 172 | # Guidance Automation Toolkit 173 | *.gpState 174 | 175 | # ReSharper is a .NET coding add-in 176 | _ReSharper*/ 177 | *.[Rr]e[Ss]harper 178 | *.DotSettings.user 179 | 180 | # TeamCity is a build add-in 181 | _TeamCity* 182 | 183 | # DotCover is a Code Coverage Tool 184 | *.dotCover 185 | 186 | # AxoCover is a Code Coverage Tool 187 | .axoCover/* 188 | !.axoCover/settings.json 189 | 190 | # Visual Studio code coverage results 191 | *.coverage 192 | *.coveragexml 193 | 194 | # NCrunch 195 | _NCrunch_* 196 | .*crunch*.local.xml 197 | nCrunchTemp_* 198 | 199 | # MightyMoose 200 | *.mm.* 201 | AutoTest.Net/ 202 | 203 | # Web workbench (sass) 204 | .sass-cache/ 205 | 206 | # Installshield output folder 207 | [Ee]xpress/ 208 | 209 | # DocProject is a documentation generator add-in 210 | DocProject/buildhelp/ 211 | DocProject/Help/*.HxT 212 | DocProject/Help/*.HxC 213 | DocProject/Help/*.hhc 214 | DocProject/Help/*.hhk 215 | DocProject/Help/*.hhp 216 | DocProject/Help/Html2 217 | DocProject/Help/html 218 | 219 | # Click-Once directory 220 | publish/ 221 | 222 | # Publish Web Output 223 | *.[Pp]ublish.xml 224 | *.azurePubxml 225 | # Note: Comment the next line if you want to checkin your web deploy settings, 226 | # but database connection strings (with potential passwords) will be unencrypted 227 | *.pubxml 228 | *.publishproj 229 | 230 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 231 | # checkin your Azure Web App publish settings, but sensitive information contained 232 | # in these scripts will be unencrypted 233 | PublishScripts/ 234 | 235 | # NuGet Packages 236 | *.nupkg 237 | # NuGet Symbol Packages 238 | *.snupkg 239 | # The packages folder can be ignored because of Package Restore 240 | **/[Pp]ackages/* 241 | # except build/, which is used as an MSBuild target. 242 | !**/[Pp]ackages/build/ 243 | # Uncomment if necessary however generally it will be regenerated when needed 244 | #!**/[Pp]ackages/repositories.config 245 | # NuGet v3's project.json files produces more ignorable files 246 | *.nuget.props 247 | *.nuget.targets 248 | 249 | # Microsoft Azure Build Output 250 | csx/ 251 | *.build.csdef 252 | 253 | # Microsoft Azure Emulator 254 | ecf/ 255 | rcf/ 256 | 257 | # Windows Store app package directories and files 258 | AppPackages/ 259 | BundleArtifacts/ 260 | Package.StoreAssociation.xml 261 | _pkginfo.txt 262 | *.appx 263 | *.appxbundle 264 | *.appxupload 265 | 266 | # Visual Studio cache files 267 | # files ending in .cache can be ignored 268 | *.[Cc]ache 269 | # but keep track of directories ending in .cache 270 | !?*.[Cc]ache/ 271 | 272 | # Others 273 | ClientBin/ 274 | ~$* 275 | *~ 276 | *.dbmdl 277 | *.dbproj.schemaview 278 | *.jfm 279 | *.pfx 280 | *.publishsettings 281 | orleans.codegen.cs 282 | 283 | # Including strong name files can present a security risk 284 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 285 | #*.snk 286 | 287 | # Since there are multiple workflows, uncomment next line to ignore bower_components 288 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 289 | #bower_components/ 290 | 291 | # RIA/Silverlight projects 292 | Generated_Code/ 293 | 294 | # Backup & report files from converting an old project file 295 | # to a newer Visual Studio version. Backup files are not needed, 296 | # because we have git ;-) 297 | _UpgradeReport_Files/ 298 | Backup*/ 299 | UpgradeLog*.XML 300 | UpgradeLog*.htm 301 | ServiceFabricBackup/ 302 | *.rptproj.bak 303 | 304 | # SQL Server files 305 | *.mdf 306 | *.ldf 307 | *.ndf 308 | 309 | # Business Intelligence projects 310 | *.rdl.data 311 | *.bim.layout 312 | *.bim_*.settings 313 | *.rptproj.rsuser 314 | *- [Bb]ackup.rdl 315 | *- [Bb]ackup ([0-9]).rdl 316 | *- [Bb]ackup ([0-9][0-9]).rdl 317 | 318 | # Microsoft Fakes 319 | FakesAssemblies/ 320 | 321 | # GhostDoc plugin setting file 322 | *.GhostDoc.xml 323 | 324 | # Node.js Tools for Visual Studio 325 | .ntvs_analysis.dat 326 | node_modules/ 327 | 328 | # Visual Studio 6 build log 329 | *.plg 330 | 331 | # Visual Studio 6 workspace options file 332 | *.opt 333 | 334 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 335 | *.vbw 336 | 337 | # Visual Studio LightSwitch build output 338 | **/*.HTMLClient/GeneratedArtifacts 339 | **/*.DesktopClient/GeneratedArtifacts 340 | **/*.DesktopClient/ModelManifest.xml 341 | **/*.Server/GeneratedArtifacts 342 | **/*.Server/ModelManifest.xml 343 | _Pvt_Extensions 344 | 345 | # Paket dependency manager 346 | .paket/paket.exe 347 | paket-files/ 348 | 349 | # FAKE - F# Make 350 | .fake/ 351 | 352 | # CodeRush personal settings 353 | .cr/personal 354 | 355 | # Python Tools for Visual Studio (PTVS) 356 | __pycache__/ 357 | *.pyc 358 | 359 | # Cake - Uncomment if you are using it 360 | # tools/** 361 | # !tools/packages.config 362 | 363 | # Tabs Studio 364 | *.tss 365 | 366 | # Telerik's JustMock configuration file 367 | *.jmconfig 368 | 369 | # BizTalk build output 370 | *.btp.cs 371 | *.btm.cs 372 | *.odx.cs 373 | *.xsd.cs 374 | 375 | # OpenCover UI analysis results 376 | OpenCover/ 377 | 378 | # Azure Stream Analytics local run output 379 | ASALocalRun/ 380 | 381 | # MSBuild Binary and Structured Log 382 | *.binlog 383 | 384 | # NVidia Nsight GPU debugger configuration file 385 | *.nvuser 386 | 387 | # MFractors (Xamarin productivity tool) working folder 388 | .mfractor/ 389 | 390 | # Local History for Visual Studio 391 | .localhistory/ 392 | 393 | # BeatPulse healthcheck temp database 394 | healthchecksdb 395 | 396 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 397 | MigrationBackup/ 398 | 399 | # Ionide (cross platform F# VS Code tools) working folder 400 | .ionide/ 401 | .git -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # v1.0.0 2 | - speed improvements 3 | - software composite tv out added 4 | 5 | # v0.5.0 6 | - hdmi / vga / tft / tv composite versions added 7 | - filebrowser reworked 8 | - speed improvements 9 | 10 | 11 | # v0.1.0 12 | - new video driver by @AlexEkb4ever 13 | - Huge speed optimizations 14 | 15 | # v0.0.2a 16 | 17 | Optimizations 18 | 19 | # v0.0.2 20 | 21 | - more games are playable. interlace and frameskip are on by default 22 | - updated ingame menu 23 | - ps2 keyboard support 24 | - *.md files support 25 | 26 | # v0.0.1 27 | 28 | First release. Games are playable only at extreme overclocking 412Mhz. Lower overclocking profiles needs frameskip and/or interlaced mode. 29 | All settings available through SELECT+START menu. 30 | Sound will not be ever available. 31 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | set(PICO_BOARD_HEADER_DIRS ${CMAKE_CURRENT_LIST_DIR}/boards ) 3 | set(PICO_BOARD "murmulator" CACHE STRING "Board type") 4 | include(pico_sdk_import.cmake) 5 | 6 | project(genesis C CXX ASM) 7 | SET(BUILD_NAME "${PROJECT_NAME}") 8 | 9 | option(I2S "Enable I2S sound" OFF) 10 | option(I2S_CS4334 "Enable I2S CS4334 sound" OFF) 11 | option(VGA "Enable VGA" OFF) 12 | option(TFT "Enable TFT display" OFF) 13 | option(ILI9341 "Enable TFT ILI9341 display" OFF) 14 | option(HDMI "Enable HDMI display" OFF) 15 | option(TV "Enable TV composite output" OFF) 16 | option(SOFTTV "Enable TV soft composite output" OFF) 17 | 18 | set(CMAKE_C_STANDARD 11) 19 | set(CMAKE_CXX_STANDARD 17) 20 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -funroll-loops -ffast-math -feliminate-unused-debug-types -ffunction-sections -fdata-sections -O2") 21 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -funroll-loops -ffast-math -feliminate-unused-debug-types -ffunction-sections -fdata-sections -O2") 22 | 23 | set(OUTPUT_DIR "${CMAKE_SOURCE_DIR}/bin/${PICO_PLATFORM}/${CMAKE_BUILD_TYPE}") 24 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${OUTPUT_DIR}") 25 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${OUTPUT_DIR}") 26 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${OUTPUT_DIR}") 27 | 28 | pico_sdk_init() 29 | 30 | add_subdirectory(drivers/audio) 31 | 32 | add_subdirectory(drivers/ps2kbd) 33 | add_subdirectory(drivers/nespad) 34 | add_subdirectory(drivers/fatfs) 35 | add_subdirectory(drivers/sdcard) 36 | 37 | add_subdirectory(drivers/vga-nextgen) 38 | add_subdirectory(drivers/st7789) 39 | add_subdirectory(drivers/hdmi) 40 | add_subdirectory(drivers/tv) 41 | add_subdirectory(drivers/tv-software) 42 | 43 | add_subdirectory(drivers/graphics) 44 | 45 | # INCLUDE FILES THAT SHOULD BE COMPILED: 46 | file(GLOB_RECURSE SRC "src/*.cpp" "src/*.c") 47 | 48 | message(STATUS "Add source files:") 49 | foreach (SRC_FILE IN LISTS SRC) 50 | message(STATUS "${SRC_FILE}") 51 | endforeach () 52 | message(STATUS "") 53 | 54 | add_executable(${PROJECT_NAME} ${SRC}) 55 | 56 | pico_define_boot_stage2(slower_boot2 ${PICO_DEFAULT_BOOT_STAGE2_FILE}) 57 | target_compile_definitions(slower_boot2 PRIVATE PICO_FLASH_SPI_CLKDIV=4) 58 | pico_set_boot_stage2(${PROJECT_NAME} slower_boot2) 59 | 60 | 61 | set(FAMILY rp2040) 62 | set(BOARD pico_sdk) 63 | 64 | pico_set_program_name(genesis "Sega MegaDrive by xrip for MURMULATOR devboard") 65 | pico_set_program_version(genesis "develop") 66 | 67 | target_include_directories(${PROJECT_NAME} PRIVATE src) 68 | 69 | IF(TFT) 70 | target_link_libraries(${PROJECT_NAME} PRIVATE st7789) 71 | target_compile_definitions(${PROJECT_NAME} PRIVATE TFT) 72 | SET(BUILD_NAME "${BUILD_NAME}-TFT") 73 | IF(ILI9341) 74 | SET(BUILD_NAME "${BUILD_NAME}-ILI9341") 75 | target_compile_definitions(${PROJECT_NAME} PRIVATE ILI9341) 76 | ELSE() 77 | SET(BUILD_NAME "${BUILD_NAME}-ST7789") 78 | ENDIF() 79 | ELSEIF(HDMI) 80 | target_link_libraries(${PROJECT_NAME} PRIVATE hdmi) 81 | target_compile_definitions(${PROJECT_NAME} PRIVATE HDMI) 82 | SET(BUILD_NAME "${BUILD_NAME}-HDMI") 83 | ELSEIF(TV) 84 | target_compile_definitions(${PROJECT_NAME} PRIVATE TV) 85 | target_link_libraries(${PROJECT_NAME} PRIVATE tv) 86 | SET(BUILD_NAME "${BUILD_NAME}-TV") 87 | ELSEIF(SOFTTV) 88 | target_compile_definitions(${PROJECT_NAME} PRIVATE SOFTTV) 89 | target_link_libraries(${PROJECT_NAME} PRIVATE tv-software) 90 | SET(BUILD_NAME "${BUILD_NAME}-TV-SOFT") 91 | ELSE() 92 | target_compile_definitions(${PROJECT_NAME} PRIVATE VGA) 93 | target_link_libraries(${PROJECT_NAME} PRIVATE vga-nextgen) 94 | SET(BUILD_NAME "${BUILD_NAME}-VGA") 95 | ENDIF() 96 | 97 | IF(NOT I2S) 98 | target_compile_definitions(${PROJECT_NAME} PRIVATE AUDIO_PWM) 99 | ELSEIF(I2S_CS4334) 100 | target_compile_definitions(${PROJECT_NAME} PRIVATE I2S_CS4334) 101 | SET(BUILD_NAME "${BUILD_NAME}-I2S-CS4334") 102 | ELSE() 103 | SET(BUILD_NAME "${BUILD_NAME}-I2S-TDA1387") 104 | ENDIF() 105 | 106 | set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "${BUILD_NAME}") 107 | 108 | target_link_libraries(${PROJECT_NAME} PRIVATE 109 | graphics 110 | audio 111 | 112 | nespad 113 | sdcard 114 | ps2kbd 115 | fatfs 116 | 117 | pico_runtime 118 | pico_stdlib 119 | pico_multicore 120 | hardware_dma 121 | hardware_pio 122 | hardware_i2c 123 | hardware_interp 124 | hardware_timer 125 | hardware_clocks 126 | hardware_pwm 127 | hardware_flash 128 | 129 | tinyusb_host 130 | ) 131 | 132 | target_link_options(${PROJECT_NAME} PRIVATE -Xlinker --print-memory-usage --data-sections) 133 | pico_add_extra_outputs(${PROJECT_NAME}) 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Raspberry Pi Pico Sega Megardrive / Sega Genesis emulator for MURMULATOR devboard 2 | 3 | Sega Megardrive / Sega Genesis emulator based on [Gwenesis Emulator](https://github.com/bzhxx/gwenesis) for RP2040 based [MURMULATOR](https://github.com/AlexEkb4ever/MURMULATOR_classical_scheme) devboard 4 | 5 | Murmulator devboard have MicroSD card slot, PS/2 keyboard input and VGA output 6 | 7 | SELECT+START launches ingame menu with frameskip and interleave options. 8 | 9 | 10 | # Hardware needed 11 | To get it working you should have an Murmulator (development) board with VGA output. Schematics available here at https://github.com/AlexEkb4ever/MURMULATOR_classical_scheme 12 | ![Murmulator Schematics](https://github.com/javavi/pico-infonesPlus/blob/main/assets/Murmulator-1_BSchem.JPG) 13 | -------------------------------------------------------------------------------- /boards/murmulator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #if PICO_RP2350 3 | #include "boards/pico2.h" 4 | 5 | #else 6 | #include "boards/pico.h" 7 | #endif 8 | // 16MB flash 9 | #define PICO_FLASH_SIZE_BYTES 16777216 10 | 11 | // SDCARD 12 | #define SDCARD_PIN_SPI0_CS 5 13 | #define SDCARD_PIN_SPI0_SCK 2 14 | #define SDCARD_PIN_SPI0_MOSI 3 15 | #define SDCARD_PIN_SPI0_MISO 4 16 | 17 | // PS2KBD 18 | #define PS2KBD_GPIO_FIRST 0 19 | 20 | // NES Gamepad 21 | #define NES_GPIO_CLK 14 22 | #define NES_GPIO_DATA 16 23 | #define NES_GPIO_LAT 15 24 | 25 | // VGA 8 pins starts from pin: 26 | #define VGA_BASE_PIN 6 27 | 28 | // HDMI 8 pins starts from pin: 29 | #define HDMI_BASE_PIN 6 30 | 31 | // TFT 32 | #define TFT_CS_PIN 6 33 | #define TFT_RST_PIN 8 34 | #define TFT_LED_PIN 9 35 | #define TFT_DC_PIN 10 36 | #define TFT_DATA_PIN 12 37 | #define TFT_CLK_PIN 13 38 | 39 | 40 | // Sound 41 | #if defined(AUDIO_PWM) 42 | #define AUDIO_PWM_PIN 26 43 | #else 44 | // I2S Sound 45 | #define AUDIO_DATA_PIN 26 46 | #define AUDIO_CLOCK_PIN 27 47 | #endif -------------------------------------------------------------------------------- /boards/murmulator2.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #if PICO_RP2350 3 | #include "boards/pico2.h" 4 | #else 5 | #include "boards/pico.h" 6 | #endif 7 | 8 | // 16MB flash 9 | #define PICO_FLASH_SIZE_BYTES 16777216 10 | 11 | // SDCARD 12 | #define SDCARD_PIN_SPI0_CS 5 13 | #define SDCARD_PIN_SPI0_SCK 6 14 | #define SDCARD_PIN_SPI0_MOSI 7 15 | #define SDCARD_PIN_SPI0_MISO 4 16 | 17 | // PS2KBD 18 | #define PS2KBD_GPIO_FIRST 2 19 | 20 | // NES Gamepad 21 | #define NES_GPIO_CLK 20 22 | #define NES_GPIO_DATA 26 23 | #define NES_GPIO_LAT 21 24 | 25 | // VGA 8 pins starts from pin: 26 | #define VGA_BASE_PIN 12 27 | 28 | // HDMI 8 pins starts from pin: 29 | #define HDMI_BASE_PIN 12 30 | 31 | // TFT 32 | #define TFT_CS_PIN 12 33 | #define TFT_RST_PIN 14 34 | #define TFT_LED_PIN 15 35 | #define TFT_DC_PIN 16 36 | #define TFT_DATA_PIN 18 37 | #define TFT_CLK_PIN 19 38 | 39 | 40 | // Sound 41 | #if defined(AUDIO_PWM) 42 | #define AUDIO_PWM_PIN 9 43 | #else 44 | #define AUDIO_DATA_PIN 9 45 | #define AUDIO_CLOCK_PIN 10 46 | #endif -------------------------------------------------------------------------------- /drivers/audio/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(audio INTERFACE) 2 | 3 | target_sources(audio INTERFACE 4 | ${CMAKE_CURRENT_LIST_DIR}/audio.c 5 | ${CMAKE_CURRENT_LIST_DIR}/audio.h 6 | ) 7 | 8 | target_link_libraries(audio INTERFACE hardware_pio hardware_clocks) 9 | 10 | target_include_directories(audio INTERFACE 11 | ${CMAKE_CURRENT_LIST_DIR} 12 | ) 13 | 14 | pico_generate_pio_header(audio 15 | ${CMAKE_CURRENT_LIST_DIR}/audio_i2s.pio 16 | ) 17 | -------------------------------------------------------------------------------- /drivers/audio/audio.c: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2022 Vincent Mistler 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #define PWM_PIN0 (AUDIO_PWM_PIN&0xfe) 26 | #define PWM_PIN1 (PWM_PIN0+1) 27 | 28 | #include "audio.h" 29 | 30 | #ifdef AUDIO_PWM_PIN 31 | #include "hardware/pwm.h" 32 | #include "hardware/clocks.h" 33 | #endif 34 | 35 | /** 36 | * return the default i2s context used to store information about the setup 37 | */ 38 | i2s_config_t i2s_get_default_config(void) { 39 | i2s_config_t i2s_config = { 40 | .sample_freq = 44100, 41 | .channel_count = 2, 42 | .data_pin = AUDIO_DATA_PIN, 43 | .clock_pin_base = AUDIO_CLOCK_PIN, 44 | .pio = pio1, 45 | .sm = 0, 46 | .dma_channel = 0, 47 | .dma_buf = NULL, 48 | .dma_trans_count = 0, 49 | .volume = 0, 50 | }; 51 | 52 | return i2s_config; 53 | } 54 | 55 | /** 56 | * Initialize the I2S driver. Must be called before calling i2s_write or i2s_dma_write 57 | * i2s_config: I2S context obtained by i2s_get_default_config() 58 | */ 59 | 60 | 61 | void i2s_init(i2s_config_t *i2s_config) { 62 | 63 | 64 | #ifndef AUDIO_PWM_PIN 65 | 66 | uint8_t func=GPIO_FUNC_PIO1; // TODO: GPIO_FUNC_PIO0 for pio0 or GPIO_FUNC_PIO1 for pio1 67 | gpio_set_function(i2s_config->data_pin, func); 68 | gpio_set_function(i2s_config->clock_pin_base, func); 69 | gpio_set_function(i2s_config->clock_pin_base+1, func); 70 | 71 | i2s_config->sm = pio_claim_unused_sm(i2s_config->pio, true); 72 | 73 | /* Set PIO clock */ 74 | uint32_t system_clock_frequency = clock_get_hz(clk_sys); 75 | uint32_t divider = system_clock_frequency * 4 / i2s_config->sample_freq; // avoid arithmetic overflow 76 | 77 | #ifdef I2S_CS4334 78 | uint offset = pio_add_program(i2s_config->pio, &audio_i2s_cs4334_program); 79 | audio_i2s_cs4334_program_init(i2s_config->pio, i2s_config->sm , offset, i2s_config->data_pin , i2s_config->clock_pin_base); 80 | divider >>= 3; 81 | #else 82 | uint offset = pio_add_program(i2s_config->pio, &audio_i2s_program); 83 | audio_i2s_program_init(i2s_config->pio, i2s_config->sm , offset, i2s_config->data_pin , i2s_config->clock_pin_base); 84 | 85 | #endif 86 | 87 | pio_sm_set_clkdiv_int_frac(i2s_config->pio, i2s_config->sm , divider >> 8u, divider & 0xffu); 88 | 89 | pio_sm_set_enabled(i2s_config->pio, i2s_config->sm, false); 90 | #endif 91 | /* Allocate memory for the DMA buffer */ 92 | i2s_config->dma_buf=malloc(i2s_config->dma_trans_count*sizeof(uint32_t)); 93 | 94 | /* Direct Memory Access setup */ 95 | i2s_config->dma_channel = dma_claim_unused_channel(true); 96 | 97 | dma_channel_config dma_config = dma_channel_get_default_config(i2s_config->dma_channel); 98 | channel_config_set_read_increment(&dma_config, true); 99 | channel_config_set_write_increment(&dma_config, false); 100 | 101 | channel_config_set_transfer_data_size(&dma_config, DMA_SIZE_32); 102 | 103 | uint32_t* addr_write_DMA=&(i2s_config->pio->txf[i2s_config->sm]); 104 | #ifdef AUDIO_PWM_PIN 105 | gpio_set_function(PWM_PIN0, GPIO_FUNC_PWM); 106 | gpio_set_function(PWM_PIN1, GPIO_FUNC_PWM); 107 | uint slice_num = pwm_gpio_to_slice_num(PWM_PIN0); 108 | 109 | 110 | 111 | pwm_config c_pwm=pwm_get_default_config(); 112 | pwm_config_set_clkdiv(&c_pwm,1.0); 113 | //pwm_config_set_wrap(&c_pwm,(1<<12)-1);//MAX PWM value 114 | pwm_config_set_wrap(&c_pwm,clock_get_hz(clk_sys)/(i2s_config->sample_freq));//MAX PWM value 115 | pwm_init(slice_num,&c_pwm,true); 116 | 117 | //Для синхронизации используем другой произвольный канал ШИМ 118 | 119 | 120 | channel_config_set_dreq(&dma_config, pwm_get_dreq(slice_num)); 121 | 122 | 123 | addr_write_DMA=(uint32_t*)&pwm_hw->slice[slice_num].cc; 124 | #else 125 | channel_config_set_dreq(&dma_config, pio_get_dreq(i2s_config->pio, i2s_config->sm, true)); 126 | #endif 127 | 128 | dma_channel_configure(i2s_config->dma_channel, 129 | &dma_config, 130 | addr_write_DMA, // Destination pointer 131 | i2s_config->dma_buf, // Source pointer 132 | i2s_config->dma_trans_count, // Number of 32 bits words to transfer 133 | false // Start immediately 134 | ); 135 | 136 | pio_sm_set_enabled(i2s_config->pio, i2s_config->sm , true); 137 | } 138 | 139 | /** 140 | * Write samples to I2S directly and wait for completion (blocking) 141 | * i2s_config: I2S context obtained by i2s_get_default_config() 142 | * sample: pointer to an array of len x 32 bits samples 143 | * Each 32 bits sample contains 2x16 bits samples, 144 | * one for the left channel and one for the right channel 145 | * len: length of sample in 32 bits words 146 | */ 147 | void i2s_write(const i2s_config_t *i2s_config,const int16_t *samples,const size_t len) { 148 | for(size_t i=0;ipio, i2s_config->sm, (uint32_t)samples[i]); 150 | } 151 | } 152 | 153 | /** 154 | * Write samples to DMA buffer and initiate DMA transfer (non blocking) 155 | * i2s_config: I2S context obtained by i2s_get_default_config() 156 | * sample: pointer to an array of dma_trans_count x 32 bits samples 157 | */ 158 | void i2s_dma_write(i2s_config_t *i2s_config,const int16_t *samples) { 159 | /* Wait the completion of the previous DMA transfer */ 160 | dma_channel_wait_for_finish_blocking(i2s_config->dma_channel); 161 | /* Copy samples into the DMA buffer */ 162 | 163 | #ifdef AUDIO_PWM_PIN 164 | for(uint16_t i=0;idma_trans_count*2;i++) { 165 | 166 | i2s_config->dma_buf[i] = (65536/2+(samples[i]))>>(4+i2s_config->volume); 167 | 168 | } 169 | #else 170 | 171 | if(i2s_config->volume==0) { 172 | memcpy(i2s_config->dma_buf,samples,i2s_config->dma_trans_count*sizeof(int32_t)); 173 | } else { 174 | for(uint16_t i=0;idma_trans_count*2;i++) { 175 | i2s_config->dma_buf[i] = samples[i]>>i2s_config->volume; 176 | } 177 | } 178 | #endif 179 | 180 | 181 | /* Initiate the DMA transfer */ 182 | dma_channel_transfer_from_buffer_now(i2s_config->dma_channel, 183 | i2s_config->dma_buf, 184 | i2s_config->dma_trans_count); 185 | } 186 | 187 | /** 188 | * Adjust the output volume 189 | * i2s_config: I2S context obtained by i2s_get_default_config() 190 | * volume: desired volume between 0 (highest. volume) and 16 (lowest volume) 191 | */ 192 | void i2s_volume(i2s_config_t *i2s_config,uint8_t volume) { 193 | if(volume>16) volume=16; 194 | i2s_config->volume=volume; 195 | } 196 | 197 | /** 198 | * Increases the output volume 199 | */ 200 | void i2s_increase_volume(i2s_config_t *i2s_config) { 201 | if(i2s_config->volume>0) { 202 | i2s_config->volume--; 203 | } 204 | } 205 | 206 | /** 207 | * Decreases the output volume 208 | */ 209 | void i2s_decrease_volume(i2s_config_t *i2s_config) { 210 | if(i2s_config->volume<16) { 211 | i2s_config->volume++; 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /drivers/audio/audio.h: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2022 Vincent Mistler 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | 25 | #pragma once 26 | #ifdef __cplusplus 27 | extern "C" { 28 | #endif 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include "audio_i2s.pio.h" 36 | 37 | typedef struct i2s_config 38 | { 39 | uint32_t sample_freq; 40 | uint16_t channel_count; 41 | uint8_t data_pin; 42 | uint8_t clock_pin_base; 43 | PIO pio; 44 | uint8_t sm; 45 | uint8_t dma_channel; 46 | uint16_t dma_trans_count; 47 | uint16_t *dma_buf; 48 | uint8_t volume; 49 | } i2s_config_t; 50 | 51 | 52 | i2s_config_t i2s_get_default_config(void); 53 | void i2s_init(i2s_config_t *i2s_config); 54 | void i2s_write(const i2s_config_t *i2s_config,const int16_t *samples,const size_t len); 55 | void i2s_dma_write(i2s_config_t *i2s_config,const int16_t *samples); 56 | void i2s_volume(i2s_config_t *i2s_config,uint8_t volume); 57 | void i2s_increase_volume(i2s_config_t *i2s_config); 58 | void i2s_decrease_volume(i2s_config_t *i2s_config); 59 | 60 | #ifdef __cplusplus 61 | } 62 | #endif 63 | -------------------------------------------------------------------------------- /drivers/audio/audio_i2s.pio: -------------------------------------------------------------------------------- 1 | ; 2 | ; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | ; 4 | ; SPDX-License-Identifier: BSD-3-Clause 5 | ; 6 | 7 | ; Transmit a mono or stereo I2S audio stream as stereo 8 | ; This is 16 bits per sample; can be altered by modifying the "set" params, 9 | ; or made programmable by replacing "set x" with "mov x, y" and using Y as a config register. 10 | ; 11 | ; Autopull must be enabled, with threshold set to 32. 12 | ; Since I2S is MSB-first, shift direction should be to left. 13 | ; Hence the format of the FIFO word is: 14 | ; 15 | ; | 31 : 16 | 15 : 0 | 16 | ; | sample ws=0 | sample ws=1 | 17 | ; 18 | ; Data is output at 1 bit per clock. Use clock divider to adjust frequency. 19 | ; Fractional divider will probably be needed to get correct bit clock period, 20 | ; but for common syslck freqs this should still give a constant word select period. 21 | ; 22 | ; One output pin is used for the data output. 23 | ; Two side-set pins are used. Bit 0 is clock, bit 1 is word select. 24 | 25 | ; Send 16 bit words to the PIO for mono, 32 bit words for stereo 26 | 27 | .program audio_i2s 28 | .side_set 2 29 | 30 | ; /--- LRCLK 31 | ; |/-- BCLK 32 | bitloop1: ; || 33 | out pins, 1 side 0b10 34 | jmp x-- bitloop1 side 0b11 35 | out pins, 1 side 0b00 36 | set x, 14 side 0b01 37 | 38 | bitloop0: 39 | out pins, 1 side 0b00 40 | jmp x-- bitloop0 side 0b01 41 | out pins, 1 side 0b10 42 | public entry_point: 43 | set x, 14 side 0b11 44 | 45 | .program audio_i2s_cs4334 46 | .side_set 2 47 | public entry_point: 48 | out pins, 31 side 0b11 49 | .wrap_target 50 | loop: 51 | set y,4 side 0b10 52 | set x,15 side 0b11 53 | l0: 54 | out pins, 1 side 0b10 55 | nop side 0b11 56 | l1: 57 | nop side 0b10 58 | jmp y--,l1 side 0b11 59 | set y,5 side 0b10 60 | jmp x--,l0 side 0b11 61 | 62 | set y,4 side 0b00 63 | set x,15 side 0b01 64 | 65 | l2: 66 | out pins, 1 side 0b00 67 | nop side 0b01 68 | l3: 69 | nop side 0b00 70 | jmp y--,l3 side 0b01 71 | set y,5 side 0b00 72 | 73 | jmp x--,l2 side 0b01 74 | 75 | % c-sdk { 76 | 77 | static inline void audio_i2s_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base) { 78 | pio_sm_config sm_config = audio_i2s_program_get_default_config(offset); 79 | 80 | sm_config_set_out_pins(&sm_config, data_pin, 1); 81 | sm_config_set_sideset_pins(&sm_config, clock_pin_base); 82 | sm_config_set_out_shift(&sm_config, false, true, 32); 83 | 84 | pio_sm_init(pio, sm, offset, &sm_config); 85 | 86 | uint pin_mask = (1u << data_pin) | (3u << clock_pin_base); 87 | pio_sm_set_pindirs_with_mask(pio, sm, pin_mask, pin_mask); 88 | pio_sm_set_pins(pio, sm, 0); // clear pins 89 | 90 | pio_sm_exec(pio, sm, pio_encode_jmp(offset + audio_i2s_offset_entry_point)); 91 | } 92 | 93 | 94 | static inline void audio_i2s_cs4334_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base) { 95 | pio_sm_config sm_config = audio_i2s_cs4334_program_get_default_config(offset); 96 | 97 | sm_config_set_out_pins(&sm_config, data_pin, 1); 98 | sm_config_set_sideset_pins(&sm_config, clock_pin_base); 99 | sm_config_set_out_shift(&sm_config, false, true, 32); 100 | 101 | pio_sm_init(pio, sm, offset, &sm_config); 102 | 103 | uint pin_mask = (1u << data_pin) | (3u << clock_pin_base); 104 | pio_sm_set_pindirs_with_mask(pio, sm, pin_mask, pin_mask); 105 | pio_sm_set_pins(pio, sm, 0); // clear pins 106 | 107 | //pio_sm_exec(pio, sm, pio_encode_jmp(offset + 0)); 108 | } 109 | %} -------------------------------------------------------------------------------- /drivers/fatfs/00readme.txt: -------------------------------------------------------------------------------- 1 | FatFs Module Source Files R0.14b 2 | 3 | 4 | FILES 5 | 6 | 00readme.txt This file. 7 | 00history.txt Revision history. 8 | ff.c FatFs module. 9 | ffconf.h Configuration file of FatFs module. 10 | ff.h Common include file for FatFs and application module. 11 | diskio.h Common include file for FatFs and disk I/O module. 12 | diskio.c An example of glue function to attach existing disk I/O module to FatFs. 13 | ffunicode.c Optional Unicode utility functions. 14 | ffsystem.c An example of optional O/S related functions. 15 | 16 | 17 | Low level disk I/O module is not included in this archive because the FatFs 18 | module is only a generic file system layer and it does not depend on any specific 19 | storage device. You need to provide a low level disk I/O module written to 20 | control the storage device that attached to the target system. 21 | 22 | -------------------------------------------------------------------------------- /drivers/fatfs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(${CMAKE_CURRENT_LIST_DIR}/fatfs.cmake) -------------------------------------------------------------------------------- /drivers/fatfs/diskio.h: -------------------------------------------------------------------------------- 1 | /*-----------------------------------------------------------------------/ 2 | / Low level disk interface modlue include file (C)ChaN, 2019 / 3 | /-----------------------------------------------------------------------*/ 4 | 5 | #ifndef _DISKIO_DEFINED 6 | #define _DISKIO_DEFINED 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | /* Status of Disk Functions */ 13 | typedef BYTE DSTATUS; 14 | 15 | /* Results of Disk Functions */ 16 | typedef enum { 17 | RES_OK = 0, /* 0: Successful */ 18 | RES_ERROR, /* 1: R/W Error */ 19 | RES_WRPRT, /* 2: Write Protected */ 20 | RES_NOTRDY, /* 3: Not Ready */ 21 | RES_PARERR /* 4: Invalid Parameter */ 22 | } DRESULT; 23 | 24 | 25 | /*---------------------------------------*/ 26 | /* Prototypes for disk control functions */ 27 | 28 | 29 | DSTATUS disk_initialize (BYTE pdrv); 30 | DSTATUS disk_status (BYTE pdrv); 31 | DRESULT disk_read (BYTE pdrv, BYTE* buff, LBA_t sector, UINT count); 32 | DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count); 33 | DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff); 34 | 35 | 36 | /* Disk Status Bits (DSTATUS) */ 37 | 38 | #define STA_NOINIT 0x01 /* Drive not initialized */ 39 | #define STA_NODISK 0x02 /* No medium in the drive */ 40 | #define STA_PROTECT 0x04 /* Write protected */ 41 | 42 | 43 | /* Command code for disk_ioctrl fucntion */ 44 | 45 | /* Generic command (Used by FatFs) */ 46 | #define CTRL_SYNC 0 /* Complete pending write process (needed at FF_FS_READONLY == 0) */ 47 | #define GET_SECTOR_COUNT 1 /* Get media size (needed at FF_USE_MKFS == 1) */ 48 | #define GET_SECTOR_SIZE 2 /* Get sector size (needed at FF_MAX_SS != FF_MIN_SS) */ 49 | #define GET_BLOCK_SIZE 3 /* Get erase block size (needed at FF_USE_MKFS == 1) */ 50 | #define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at FF_USE_TRIM == 1) */ 51 | 52 | /* Generic command (Not used by FatFs) */ 53 | #define CTRL_POWER 5 /* Get/Set power status */ 54 | #define CTRL_LOCK 6 /* Lock/Unlock media removal */ 55 | #define CTRL_EJECT 7 /* Eject media */ 56 | #define CTRL_FORMAT 8 /* Create physical format on the media */ 57 | 58 | /* MMC/SDC specific ioctl command */ 59 | #define MMC_GET_TYPE 10 /* Get card type */ 60 | #define MMC_GET_CSD 11 /* Get CSD */ 61 | #define MMC_GET_CID 12 /* Get CID */ 62 | #define MMC_GET_OCR 13 /* Get OCR */ 63 | #define MMC_GET_SDSTAT 14 /* Get SD status */ 64 | #define ISDIO_READ 55 /* Read data form SD iSDIO register */ 65 | #define ISDIO_WRITE 56 /* Write data to SD iSDIO register */ 66 | #define ISDIO_MRITE 57 /* Masked write data to SD iSDIO register */ 67 | 68 | /* ATA/CF specific ioctl command */ 69 | #define ATA_GET_REV 20 /* Get F/W revision */ 70 | #define ATA_GET_MODEL 21 /* Get model name */ 71 | #define ATA_GET_SN 22 /* Get serial number */ 72 | 73 | #ifdef __cplusplus 74 | } 75 | #endif 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /drivers/fatfs/f_util.c: -------------------------------------------------------------------------------- 1 | /* f_util.c 2 | Copyright 2021 Carl John Kugler III 3 | 4 | Licensed under the Apache License, Version 2.0 (the License); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | */ 14 | #include "ff.h" 15 | 16 | const char *FRESULT_str(FRESULT i) { 17 | switch (i) { 18 | case FR_OK: 19 | return "Succeeded"; 20 | case FR_DISK_ERR: 21 | return "A hard error occurred in the low level disk I/O layer"; 22 | case FR_INT_ERR: 23 | return "Assertion failed"; 24 | case FR_NOT_READY: 25 | return "The physical drive cannot work"; 26 | case FR_NO_FILE: 27 | return "Could not find the file"; 28 | case FR_NO_PATH: 29 | return "Could not find the path"; 30 | case FR_INVALID_NAME: 31 | return "The path name format is invalid"; 32 | case FR_DENIED: 33 | return "Access denied due to prohibited access or directory full"; 34 | case FR_EXIST: 35 | return "Access denied due to prohibited access (exists)"; 36 | case FR_INVALID_OBJECT: 37 | return "The file/directory object is invalid"; 38 | case FR_WRITE_PROTECTED: 39 | return "The physical drive is write protected"; 40 | case FR_INVALID_DRIVE: 41 | return "The logical drive number is invalid"; 42 | case FR_NOT_ENABLED: 43 | return "The volume has no work area (mount)"; 44 | case FR_NO_FILESYSTEM: 45 | return "There is no valid FAT volume"; 46 | case FR_MKFS_ABORTED: 47 | return "The f_mkfs() aborted due to any problem"; 48 | case FR_TIMEOUT: 49 | return "Could not get a grant to access the volume within defined " 50 | "period"; 51 | case FR_LOCKED: 52 | return "The operation is rejected according to the file sharing " 53 | "policy"; 54 | case FR_NOT_ENOUGH_CORE: 55 | return "LFN working buffer could not be allocated"; 56 | case FR_TOO_MANY_OPEN_FILES: 57 | return "Number of open files > FF_FS_LOCK"; 58 | case FR_INVALID_PARAMETER: 59 | return "Given parameter is invalid"; 60 | default: 61 | return "Unknown"; 62 | } 63 | } 64 | 65 | FRESULT delete_node ( 66 | TCHAR* path, /* Path name buffer with the sub-directory to delete */ 67 | UINT sz_buff, /* Size of path name buffer (items) */ 68 | FILINFO* fno /* Name read buffer */ 69 | ) 70 | { 71 | UINT i, j; 72 | FRESULT fr; 73 | DIR dir; 74 | 75 | 76 | fr = f_opendir(&dir, path); /* Open the sub-directory to make it empty */ 77 | if (fr != FR_OK) return fr; 78 | 79 | for (i = 0; path[i]; i++) ; /* Get current path length */ 80 | path[i++] = '/'; 81 | 82 | for (;;) { 83 | fr = f_readdir(&dir, fno); /* Get a directory item */ 84 | if (fr != FR_OK || !fno->fname[0]) break; /* End of directory? */ 85 | j = 0; 86 | do { /* Make a path name */ 87 | if (i + j >= sz_buff) { /* Buffer over flow? */ 88 | fr = 100; break; /* Fails with 100 when buffer overflow */ 89 | } 90 | path[i + j] = fno->fname[j]; 91 | } while (fno->fname[j++]); 92 | if (fno->fattrib & AM_DIR) { /* Item is a sub-directory */ 93 | fr = delete_node(path, sz_buff, fno); 94 | } else { /* Item is a file */ 95 | fr = f_unlink(path); 96 | } 97 | if (fr != FR_OK) break; 98 | } 99 | 100 | path[--i] = 0; /* Restore the path name */ 101 | f_closedir(&dir); 102 | 103 | if (fr == FR_OK) fr = f_unlink(path); /* Delete the empty sub-directory */ 104 | return fr; 105 | } 106 | -------------------------------------------------------------------------------- /drivers/fatfs/f_util.h: -------------------------------------------------------------------------------- 1 | /* f_util.h 2 | Copyright 2021 Carl John Kugler III 3 | 4 | Licensed under the Apache License, Version 2.0 (the License); you may not use 5 | this file except in compliance with the License. You may obtain a copy of the 6 | License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | */ 14 | #pragma once 15 | #include "ff.h" 16 | 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | const char *FRESULT_str(FRESULT i); 22 | FRESULT delete_node ( 23 | TCHAR* path, /* Path name buffer with the sub-directory to delete */ 24 | UINT sz_buff, /* Size of path name buffer (items) */ 25 | FILINFO* fno /* Name read buffer */ 26 | ); 27 | 28 | #ifdef __cplusplus 29 | } 30 | #endif 31 | -------------------------------------------------------------------------------- /drivers/fatfs/fatfs.cmake: -------------------------------------------------------------------------------- 1 | if (NOT TARGET fatfs) 2 | add_library(fatfs INTERFACE) 3 | 4 | target_sources(fatfs INTERFACE 5 | ${CMAKE_CURRENT_LIST_DIR}/ff.c 6 | ${CMAKE_CURRENT_LIST_DIR}/f_util.c 7 | ${CMAKE_CURRENT_LIST_DIR}/ffsystem.c 8 | ${CMAKE_CURRENT_LIST_DIR}/ffunicode.c 9 | ) 10 | 11 | target_link_libraries(fatfs INTERFACE pico_stdlib hardware_clocks hardware_spi) 12 | target_include_directories(fatfs INTERFACE ${CMAKE_CURRENT_LIST_DIR}) 13 | 14 | endif () 15 | -------------------------------------------------------------------------------- /drivers/fatfs/ffsystem.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------*/ 2 | /* Sample Code of OS Dependent Functions for FatFs */ 3 | /* (C)ChaN, 2018 */ 4 | /*------------------------------------------------------------------------*/ 5 | 6 | 7 | #include "ff.h" 8 | 9 | 10 | #if FF_USE_LFN == 3 /* Dynamic memory allocation */ 11 | 12 | /*------------------------------------------------------------------------*/ 13 | /* Allocate a memory block */ 14 | /*------------------------------------------------------------------------*/ 15 | 16 | void* ff_memalloc ( /* Returns pointer to the allocated memory block (null if not enough core) */ 17 | UINT msize /* Number of bytes to allocate */ 18 | ) 19 | { 20 | return malloc(msize); /* Allocate a new memory block with POSIX API */ 21 | } 22 | 23 | 24 | /*------------------------------------------------------------------------*/ 25 | /* Free a memory block */ 26 | /*------------------------------------------------------------------------*/ 27 | 28 | void ff_memfree ( 29 | void* mblock /* Pointer to the memory block to free (nothing to do if null) */ 30 | ) 31 | { 32 | free(mblock); /* Free the memory block with POSIX API */ 33 | } 34 | 35 | #endif 36 | 37 | 38 | 39 | #if FF_FS_REENTRANT /* Mutal exclusion */ 40 | 41 | /*------------------------------------------------------------------------*/ 42 | /* Create a Synchronization Object */ 43 | /*------------------------------------------------------------------------*/ 44 | /* This function is called in f_mount() function to create a new 45 | / synchronization object for the volume, such as semaphore and mutex. 46 | / When a 0 is returned, the f_mount() function fails with FR_INT_ERR. 47 | */ 48 | 49 | //const osMutexDef_t Mutex[FF_VOLUMES]; /* Table of CMSIS-RTOS mutex */ 50 | 51 | 52 | int ff_cre_syncobj ( /* 1:Function succeeded, 0:Could not create the sync object */ 53 | BYTE vol, /* Corresponding volume (logical drive number) */ 54 | FF_SYNC_t* sobj /* Pointer to return the created sync object */ 55 | ) 56 | { 57 | /* Win32 */ 58 | *sobj = CreateMutex(NULL, FALSE, NULL); 59 | return (int)(*sobj != INVALID_HANDLE_VALUE); 60 | 61 | /* uITRON */ 62 | // T_CSEM csem = {TA_TPRI,1,1}; 63 | // *sobj = acre_sem(&csem); 64 | // return (int)(*sobj > 0); 65 | 66 | /* uC/OS-II */ 67 | // OS_ERR err; 68 | // *sobj = OSMutexCreate(0, &err); 69 | // return (int)(err == OS_NO_ERR); 70 | 71 | /* FreeRTOS */ 72 | // *sobj = xSemaphoreCreateMutex(); 73 | // return (int)(*sobj != NULL); 74 | 75 | /* CMSIS-RTOS */ 76 | // *sobj = osMutexCreate(&Mutex[vol]); 77 | // return (int)(*sobj != NULL); 78 | } 79 | 80 | 81 | /*------------------------------------------------------------------------*/ 82 | /* Delete a Synchronization Object */ 83 | /*------------------------------------------------------------------------*/ 84 | /* This function is called in f_mount() function to delete a synchronization 85 | / object that created with ff_cre_syncobj() function. When a 0 is returned, 86 | / the f_mount() function fails with FR_INT_ERR. 87 | */ 88 | 89 | int ff_del_syncobj ( /* 1:Function succeeded, 0:Could not delete due to an error */ 90 | FF_SYNC_t sobj /* Sync object tied to the logical drive to be deleted */ 91 | ) 92 | { 93 | /* Win32 */ 94 | return (int)CloseHandle(sobj); 95 | 96 | /* uITRON */ 97 | // return (int)(del_sem(sobj) == E_OK); 98 | 99 | /* uC/OS-II */ 100 | // OS_ERR err; 101 | // OSMutexDel(sobj, OS_DEL_ALWAYS, &err); 102 | // return (int)(err == OS_NO_ERR); 103 | 104 | /* FreeRTOS */ 105 | // vSemaphoreDelete(sobj); 106 | // return 1; 107 | 108 | /* CMSIS-RTOS */ 109 | // return (int)(osMutexDelete(sobj) == osOK); 110 | } 111 | 112 | 113 | /*------------------------------------------------------------------------*/ 114 | /* Request Grant to Access the Volume */ 115 | /*------------------------------------------------------------------------*/ 116 | /* This function is called on entering file functions to lock the volume. 117 | / When a 0 is returned, the file function fails with FR_TIMEOUT. 118 | */ 119 | 120 | int ff_req_grant ( /* 1:Got a grant to access the volume, 0:Could not get a grant */ 121 | FF_SYNC_t sobj /* Sync object to wait */ 122 | ) 123 | { 124 | /* Win32 */ 125 | return (int)(WaitForSingleObject(sobj, FF_FS_TIMEOUT) == WAIT_OBJECT_0); 126 | 127 | /* uITRON */ 128 | // return (int)(wai_sem(sobj) == E_OK); 129 | 130 | /* uC/OS-II */ 131 | // OS_ERR err; 132 | // OSMutexPend(sobj, FF_FS_TIMEOUT, &err)); 133 | // return (int)(err == OS_NO_ERR); 134 | 135 | /* FreeRTOS */ 136 | // return (int)(xSemaphoreTake(sobj, FF_FS_TIMEOUT) == pdTRUE); 137 | 138 | /* CMSIS-RTOS */ 139 | // return (int)(osMutexWait(sobj, FF_FS_TIMEOUT) == osOK); 140 | } 141 | 142 | 143 | /*------------------------------------------------------------------------*/ 144 | /* Release Grant to Access the Volume */ 145 | /*------------------------------------------------------------------------*/ 146 | /* This function is called on leaving file functions to unlock the volume. 147 | */ 148 | 149 | void ff_rel_grant ( 150 | FF_SYNC_t sobj /* Sync object to be signaled */ 151 | ) 152 | { 153 | /* Win32 */ 154 | ReleaseMutex(sobj); 155 | 156 | /* uITRON */ 157 | // sig_sem(sobj); 158 | 159 | /* uC/OS-II */ 160 | // OSMutexPost(sobj); 161 | 162 | /* FreeRTOS */ 163 | // xSemaphoreGive(sobj); 164 | 165 | /* CMSIS-RTOS */ 166 | // osMutexRelease(sobj); 167 | } 168 | 169 | #endif 170 | 171 | -------------------------------------------------------------------------------- /drivers/graphics/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(graphics INTERFACE) 2 | 3 | target_sources(graphics INTERFACE ${CMAKE_CURRENT_LIST_DIR}/graphics.c) 4 | 5 | #target_link_libraries(graphics INTERFACE 6 | #vga-nextgen 7 | #hdmi 8 | #tft 9 | #) 10 | 11 | target_include_directories(graphics INTERFACE ${CMAKE_CURRENT_LIST_DIR}) 12 | 13 | #pico_generate_pio_header(st7789 14 | # ${CMAKE_CURRENT_LIST_DIR}/st7789.pio 15 | #) 16 | -------------------------------------------------------------------------------- /drivers/graphics/graphics.c: -------------------------------------------------------------------------------- 1 | #include "graphics.h" 2 | #include 3 | 4 | void draw_text(const char string[TEXTMODE_COLS + 1], uint32_t x, uint32_t y, uint8_t color, uint8_t bgcolor) { 5 | uint8_t* t_buf = text_buffer + TEXTMODE_COLS * 2 * y + 2 * x; 6 | for (int xi = TEXTMODE_COLS * 2; xi--;) { 7 | if (!*string) break; 8 | *t_buf++ = *string++; 9 | *t_buf++ = bgcolor << 4 | color & 0xF; 10 | } 11 | } 12 | 13 | void draw_window(const char title[TEXTMODE_COLS + 1], uint32_t x, uint32_t y, uint32_t width, uint32_t height) { 14 | char line[width + 1]; 15 | memset(line, 0, sizeof line); 16 | width--; 17 | height--; 18 | // Рисуем рамки 19 | 20 | memset(line, 0xCD, width); // ═══ 21 | 22 | 23 | line[0] = 0xC9; // ╔ 24 | line[width] = 0xBB; // ╗ 25 | draw_text(line, x, y, 11, 1); 26 | 27 | line[0] = 0xC8; // ╚ 28 | line[width] = 0xBC; // ╝ 29 | draw_text(line, x, height + y, 11, 1); 30 | 31 | memset(line, ' ', width); 32 | line[0] = line[width] = 0xBA; 33 | 34 | for (int i = 1; i < height; i++) { 35 | draw_text(line, x, y + i, 11, 1); 36 | } 37 | 38 | snprintf(line, width - 1, " %s ", title); 39 | draw_text(line, x + (width - strlen(line)) / 2, y, 14, 3); 40 | } 41 | -------------------------------------------------------------------------------- /drivers/graphics/graphics.h: -------------------------------------------------------------------------------- 1 | #ifdef __cplusplus 2 | extern "C" { 3 | #endif 4 | 5 | #include "stdbool.h" 6 | #include "stdio.h" 7 | #include "stdint.h" 8 | 9 | #ifdef TFT 10 | #include "st7789.h" 11 | #endif 12 | #ifdef HDMI 13 | #include "hdmi.h" 14 | #endif 15 | #ifdef VGA 16 | #include "vga.h" 17 | #endif 18 | #ifdef TV 19 | #include "tv.h" 20 | #endif 21 | #ifdef SOFTTV 22 | #include "tv-software.h" 23 | #endif 24 | #include "font6x8.h" 25 | #include "font8x8.h" 26 | #include "font8x16.h" 27 | 28 | enum graphics_mode_t { 29 | TEXTMODE_DEFAULT, 30 | GRAPHICSMODE_DEFAULT, 31 | 32 | TEXTMODE_53x30, 33 | 34 | TEXTMODE_160x100, 35 | 36 | CGA_160x200x16, 37 | CGA_320x200x4, 38 | CGA_640x200x2, 39 | 40 | TGA_320x200x16, 41 | EGA_320x200x16x4, 42 | VGA_320x240x256, 43 | VGA_320x200x256x4, 44 | // planar VGA 45 | }; 46 | 47 | // Буффер текстового режима 48 | extern uint8_t* text_buffer; 49 | 50 | void graphics_init(); 51 | 52 | void graphics_set_mode(enum graphics_mode_t mode); 53 | 54 | void graphics_set_buffer(uint8_t* buffer, uint16_t width, uint16_t height); 55 | 56 | void graphics_set_offset(int x, int y); 57 | 58 | void graphics_set_palette(uint8_t i, uint32_t color); 59 | 60 | void graphics_set_textbuffer(uint8_t* buffer); 61 | 62 | void graphics_set_bgcolor(uint32_t color888); 63 | 64 | void graphics_set_flashmode(bool flash_line, bool flash_frame); 65 | 66 | void draw_text(const char string[TEXTMODE_COLS + 1], uint32_t x, uint32_t y, uint8_t color, uint8_t bgcolor); 67 | void draw_window(const char title[TEXTMODE_COLS + 1], uint32_t x, uint32_t y, uint32_t width, uint32_t height); 68 | 69 | void clrScr(uint8_t color); 70 | 71 | #ifdef __cplusplus 72 | } 73 | #endif 74 | -------------------------------------------------------------------------------- /drivers/hdmi/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(hdmi INTERFACE) 2 | 3 | target_sources(hdmi INTERFACE ${CMAKE_CURRENT_LIST_DIR}/hdmi.c) 4 | 5 | target_link_libraries(hdmi INTERFACE hardware_pio hardware_clocks hardware_dma) 6 | 7 | target_include_directories(hdmi INTERFACE 8 | ${CMAKE_CURRENT_LIST_DIR} 9 | ) 10 | 11 | -------------------------------------------------------------------------------- /drivers/hdmi/hdmi.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | 7 | #include "inttypes.h" 8 | #include "stdbool.h" 9 | 10 | #include "hardware/pio.h" 11 | 12 | #define PIO_VIDEO pio0 13 | #define PIO_VIDEO_ADDR pio0 14 | #define VIDEO_DMA_IRQ (DMA_IRQ_0) 15 | 16 | #ifndef HDMI_BASE_PIN 17 | #define HDMI_BASE_PIN (6) 18 | #endif 19 | 20 | #define HDMI_PIN_invert_diffpairs (1) 21 | #define HDMI_PIN_RGB_notBGR (1) 22 | #define beginHDMI_PIN_data (HDMI_BASE_PIN+2) 23 | #define beginHDMI_PIN_clk (HDMI_BASE_PIN) 24 | 25 | #define TEXTMODE_COLS 53 26 | #define TEXTMODE_ROWS 30 27 | 28 | #define RGB888(r, g, b) ((r<<16) | (g << 8 ) | b ) 29 | 30 | // TODO: Сделать настраиваемо 31 | static const uint8_t textmode_palette[16] = { 32 | 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215 33 | }; 34 | 35 | 36 | static void graphics_set_flashmode(bool flash_line, bool flash_frame) { 37 | // dummy 38 | } 39 | 40 | 41 | #ifdef __cplusplus 42 | } 43 | #endif 44 | -------------------------------------------------------------------------------- /drivers/nespad/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(nespad INTERFACE) 2 | 3 | target_sources(nespad INTERFACE 4 | ${CMAKE_CURRENT_LIST_DIR}/nespad.cpp 5 | ${CMAKE_CURRENT_LIST_DIR}/nespad.h 6 | ) 7 | 8 | target_link_libraries(nespad INTERFACE hardware_pio) 9 | 10 | target_include_directories(nespad INTERFACE 11 | ${CMAKE_CURRENT_LIST_DIR} 12 | ) 13 | -------------------------------------------------------------------------------- /drivers/nespad/nespad.cpp: -------------------------------------------------------------------------------- 1 | #include "hardware/pio.h" 2 | 3 | #define nespad_wrap_target 0 4 | #define nespad_wrap 6 5 | 6 | static const uint16_t nespad_program_instructions[] = { 7 | // .wrap_target 8 | 0x80a0, // 0: pull block 9 | 0xea01, // 1: set pins, 1 side 0 [10] 10 | 0xe02f, // 2: set x, 15 side 0 11 | 0xe000, // 3: set pins, 0 side 0 12 | 0x4402, // 4: in pins, 2 side 0 [4] <--- 2 13 | 0xf500, // 5: set pins, 0 side 1 [5] 14 | 0x0044, // 6: jmp x--, 4 side 0 15 | // .wrap 16 | }; 17 | 18 | static const struct pio_program nespad_program = { 19 | .instructions = nespad_program_instructions, 20 | .length = 7, 21 | .origin = -1, 22 | }; 23 | 24 | static inline pio_sm_config nespad_program_get_default_config(uint offset) { 25 | pio_sm_config c = pio_get_default_sm_config(); 26 | sm_config_set_wrap(&c, offset + nespad_wrap_target, offset + nespad_wrap); 27 | sm_config_set_sideset(&c, 1, false, false); 28 | return c; 29 | } 30 | 31 | static PIO pio = pio1; 32 | static uint8_t sm = -1; 33 | uint32_t nespad_state = 0; // Joystick 1 34 | uint32_t nespad_state2 = 0; // Joystick 2 35 | 36 | bool nespad_begin(uint32_t cpu_khz, uint8_t clkPin, uint8_t dataPin,uint8_t latPin) { 37 | if (pio_can_add_program(pio, &nespad_program) && 38 | ((sm = pio_claim_unused_sm(pio, true)) >= 0)) { 39 | uint offset = pio_add_program(pio, &nespad_program); 40 | pio_sm_config c = nespad_program_get_default_config(offset); 41 | 42 | sm_config_set_sideset_pins(&c, clkPin); 43 | sm_config_set_in_pins(&c, dataPin); 44 | sm_config_set_set_pins(&c, latPin, 1); 45 | pio_gpio_init(pio, clkPin); 46 | pio_gpio_init(pio, dataPin); 47 | pio_gpio_init(pio, dataPin+1); // +1 Pin for Joystick2 48 | pio_gpio_init(pio, latPin); 49 | gpio_set_pulls(dataPin, true, false); // Pull data high, 0xFF if unplugged 50 | gpio_set_pulls(dataPin+1, true, false); // Pull data high, 0xFF if unplugged for Joystick2 51 | 52 | pio_sm_set_pindirs_with_mask(pio, sm, 53 | (1 << clkPin) | (1 << latPin), // Outputs 54 | (1 << clkPin) | (1 << latPin) | 55 | (1 << dataPin) | (1 << (dataPin+1)) 56 | ); // All pins 57 | sm_config_set_in_shift(&c, true, true, 32); // R shift, autopush @ 8 bits (@ 16 bits for 2 Joystick) 58 | 59 | sm_config_set_clkdiv_int_frac(&c, cpu_khz / 1000, 0); // 1 MHz clock 60 | 61 | 62 | 63 | pio_sm_clear_fifos(pio, sm); 64 | 65 | pio_sm_init(pio, sm, offset, &c); 66 | pio_sm_set_enabled(pio, sm, true); 67 | pio->txf[sm]=0; 68 | return true; // Success 69 | } 70 | return false; 71 | } 72 | 73 | 74 | 75 | // nespad read. Ideally should be called ~100 uS after 76 | // nespad_read_start(), but can be sooner (will block until ready), or later 77 | // (will introduce latency). Sets value of global nespad_state variable, a 78 | // bitmask of button/D-pad state (1 = pressed). 0x80=Right, 0x40=Left, 79 | // 0x20=Down, 0x10=Up, 0x08=Start, 0x04=Select, 0x02=B, 0x01=A. Must first 80 | // call nespad_begin() once to set up PIO. Result will be 0 if PIO failed to 81 | // init (e.g. no free state machine). 82 | 83 | void nespad_read() 84 | { 85 | if (sm<0) return; 86 | if (pio_sm_is_rx_fifo_empty(pio, sm)) return; 87 | 88 | // Right-shift was used in sm config so bit order matches NES controller 89 | // bits used elsewhere in picones, but does require shifting down... 90 | uint32_t temp=pio->rxf[sm]^ 0xFFFFFFFF; 91 | pio->txf[sm]=0; 92 | nespad_state = temp & 0x555555; // Joy1 93 | nespad_state2 = temp >> 1 & 0x555555; // Joy2 94 | } 95 | 96 | 97 | -------------------------------------------------------------------------------- /drivers/nespad/nespad.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define DPAD_LEFT 0x001000 4 | #define DPAD_RIGHT 0x004000 5 | #define DPAD_DOWN 0x000400 6 | #define DPAD_UP 0x000100 7 | #define DPAD_START 0x000040 8 | #define DPAD_SELECT 0x000010 9 | #define DPAD_B 0x000004 //Y on SNES 10 | #define DPAD_A 0x000001 //B on SNES 11 | 12 | #define DPAD_Y 0x010000 //A on SNES 13 | #define DPAD_X 0x040000 14 | #define DPAD_LT 0x100000 15 | #define DPAD_RT 0x400000 16 | 17 | extern uint32_t nespad_state; // (S)NES Joystick1 18 | extern uint32_t nespad_state2; // (S)NES Joystick2 19 | 20 | extern bool nespad_begin(uint32_t cpu_khz, uint8_t clkPin, uint8_t dataPin, 21 | uint8_t latPin); 22 | 23 | 24 | extern void nespad_read(); -------------------------------------------------------------------------------- /drivers/ps2/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(ps2 INTERFACE) 2 | 3 | target_sources(ps2 INTERFACE 4 | ${CMAKE_CURRENT_LIST_DIR}/ps2.c 5 | ${CMAKE_CURRENT_LIST_DIR}/ps2.h 6 | ) 7 | 8 | target_link_libraries(ps2 INTERFACE hardware_pio hardware_clocks) 9 | 10 | target_include_directories(ps2 INTERFACE 11 | ${CMAKE_CURRENT_LIST_DIR} 12 | ) 13 | 14 | 15 | #pico_generate_pio_header(ps2 16 | # ${CMAKE_CURRENT_LIST_DIR}/ps2_mrmltr.pio 17 | #) 18 | -------------------------------------------------------------------------------- /drivers/ps2/ps2.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "strings.h" 4 | #include "stdio.h" 5 | 6 | #include 7 | 8 | #define KBD_CLOCK_PIN (0) 9 | #define KBD_DATA_PIN (1) 10 | #define KBD_BUFFER_SIZE 16 11 | 12 | #define PS2_LED_SCROLL_LOCK 1 13 | #define PS2_LED_NUM_LOCK 2 14 | #define PS2_LED_CAPS_LOCK 4 15 | 16 | extern uint8_t kbloop; 17 | 18 | void KeyboardHandler();//uint /*gpio*/, uint32_t /*event_mask*/ 19 | uint32_t ps2getcode(void); 20 | 21 | 22 | void keyboard_init(void); 23 | 24 | void Deinit_kbd(void); 25 | 26 | void keyboard_toggle_led(uint8_t led); 27 | 28 | int16_t keyboard_send(uint8_t data); 29 | 30 | struct ps2_struct_group { 31 | unsigned char character; 32 | unsigned char make; 33 | unsigned is_char; 34 | unsigned char xt_make; 35 | }; 36 | 37 | 38 | static struct ps2_struct_group ps2_group1[] = 39 | { 40 | { 'a', 0x1C, 1, 0x1E }, 41 | { 'b', 0x32, 1, 0x30 }, 42 | { 'c', 0x21, 1, 0x2E }, 43 | { 'd', 0x23, 1, 0x20 }, 44 | { 'e', 0x24, 1, 0x12 }, 45 | { 'f', 0x2B, 1, 0x21 }, 46 | { 'g', 0x34, 1, 0x22 }, 47 | { 'h', 0x33, 1, 0x23 }, 48 | { 'i', 0x43, 1, 0x17 }, 49 | { 'j', 0x3B, 1, 0x24 }, 50 | { 'k', 0x42, 1, 0x25 }, 51 | { 'l', 0x4B, 1, 0x26 }, 52 | { 'm', 0x3A, 1, 0x32 }, 53 | { 'n', 0x31, 1, 0x31 }, 54 | { 'o', 0x44, 1, 0x18 }, 55 | { 'p', 0x4D, 1, 0x19 }, 56 | { 'q', 0x15, 1, 0x10 }, 57 | { 'r', 0x2D, 1, 0x13 }, 58 | { 's', 0x1B, 1, 0x1F }, 59 | { 't', 0x2C, 1, 0x14 }, 60 | { 'u', 0x3C, 1, 0x16 }, 61 | { 'v', 0x2A, 1, 0x2F }, 62 | { 'w', 0x1D, 1, 0x11 }, 63 | { 'x', 0x22, 1, 0x2D }, 64 | { 'y', 0x35, 1, 0x15 }, 65 | { 'z', 0x1A, 1, 0x2C }, 66 | { '0', 0x45, 1, 0x0B }, 67 | { '1', 0x16, 1, 0x02 }, 68 | { '2', 0x1E, 1, 0x03 }, 69 | { '3', 0x26, 1, 0x04 }, 70 | { '4', 0x25, 1, 0x05 }, 71 | { '5', 0x2E, 1, 0x06 }, 72 | { '6', 0x36, 1, 0x07 }, 73 | { '7', 0x3D, 1, 0x08 }, 74 | { '8', 0x3E, 1, 0x09 }, 75 | { '9', 0x46, 1, 0x0A }, 76 | { '`', 0x0E, 1, 0x29 }, 77 | { '-', 0x4E, 1, 0x0C }, 78 | { '=', 0x55, 1, 0x0D }, 79 | { '\\', 0x5D, 1, 0x2B }, 80 | { '\b', 0x66, 0, 0x0E }, // backsapce 81 | { ' ', 0x29, 1, 0x39 }, // space 82 | { '\t', 0x0D, 0, 0x0F }, // tab 83 | { ' ', 0x58, 0, 0x3A }, // caps 84 | { ' ', 0x12, 0, 0x2A }, // left shift 85 | { ' ', 0x14, 0, 0x1D }, // left ctrl 86 | { ' ', 0x11, 0, 0x38 }, // left alt 87 | { ' ', 0x59, 0, 0x36 }, // right shift 88 | { '\n', 0x5A, 1, 0x1C }, // enter 89 | { ' ', 0x76, 0, 0x01 }, // esc 90 | { ' ', 0x05, 0, 0x3B }, // F1 91 | { ' ', 0x06, 0, 0x3C }, // F2 92 | { ' ', 0x04, 0, 0x3D }, // F3 93 | { ' ', 0x0C, 0, 0x3E }, // F4 94 | { ' ', 0x03, 0, 0x3F }, // F5 95 | { ' ', 0x0B, 0, 0x40 }, // F6 96 | { ' ', 0x83, 0, 0x41 }, // F7 97 | { ' ', 0x0A, 0, 0x42 }, // F8 98 | { ' ', 0x01, 0, 0x43 }, // f9 99 | { ' ', 0x09, 0, 0x44 }, // f10 100 | { ' ', 0x78, 0, 0x57 }, // f11 101 | { ' ', 0x07, 0, 0x58 }, // f12 102 | { ' ', 0x7E, 0, 0x46 }, // SCROLL 103 | { '[', 0x54, 1, 0x1A }, 104 | { ' ', 0x77, 0, 0x45 }, // Num Lock 105 | { '*', 0x7C, 1, 0x37 }, // Keypad * 106 | { '-', 0x7B, 1, 0x4A }, // Keypad - 107 | { '+', 0x79, 1, 0x4E }, // Keypad + 108 | { '.', 0x71, 1, 0x53 }, // Keypad . 109 | { '0', 0x70, 1, 0x52 }, // Keypad 0 110 | { '1', 0x69, 1, 0x4F }, // Keypad 1 111 | { '2', 0x72, 1, 0x50 }, // Keypad 2 112 | { '3', 0x7A, 1, 0x51 }, // Keypad 3 113 | { '4', 0x6B, 1, 0x4B }, // Keypad 4 114 | { '5', 0x73, 1, 0x4C }, // Keypad 5 115 | { '6', 0x74, 1, 0x4D }, // Keypad 6 116 | { '7', 0x6C, 1, 0x47 }, // Keypad 7 117 | { '8', 0x75, 1, 0x48 }, // Keypad 8 118 | { '9', 0x7D, 1, 0x49 }, // Keypad 9 119 | { ']', 0x5B, 1, 0x1B }, 120 | { ';', 0x4C, 1, 0x27 }, 121 | { '\'', 0x52, 1, 0x28 }, 122 | { ',', 0x41, 1, 0x33 }, 123 | { '.', 0x49, 1, 0x34 }, 124 | { '/', 0x4A, 1, 0x35 }, 125 | }; 126 | 127 | static struct ps2_struct_group ps2_group2[] = 128 | { 129 | { ' ', 0x5B, 0, 0x1F }, // left gui 130 | { ' ', 0x1D, 0, 0x14 }, // right ctrl 131 | { ' ', 0x5C, 0, 0x27 }, // right gui 132 | { ' ', 0x38, 0, 0x11 }, // right alt 133 | { ' ', 0x5D, 0, 0x2F }, // apps 134 | { ' ', 0x52, 0, 0x70 }, // insert 135 | { ' ', 0x47, 0, 0x6C }, // home 136 | { ' ', 0x49, 0, 0x7D }, // page up 137 | { ' ', 0x53, 0, 0x71 }, // delete 138 | { ' ', 0x4F, 0, 0x69 }, // end 139 | { ' ', 0x51, 0, 0x7A }, // page down 140 | { ' ', 0x48, 0, 0x75 }, // u arrow 141 | { ' ', 0x4B, 0, 0x6B }, // l arrow 142 | { ' ', 0x50, 0, 0x72 }, // d arrow 143 | { ' ', 0x4D, 0, 0x74 }, // r arrow 144 | { ' ', 0x1C, 0, 0x5A }, // kp en 145 | }; 146 | 147 | -------------------------------------------------------------------------------- /drivers/ps2kbd/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(ps2kbd INTERFACE) 2 | 3 | target_sources(ps2kbd INTERFACE 4 | ${CMAKE_CURRENT_LIST_DIR}/ps2kbd_mrmltr.cpp 5 | ${CMAKE_CURRENT_LIST_DIR}/ps2kbd_mrmltr.h 6 | ) 7 | 8 | target_link_libraries(ps2kbd INTERFACE hardware_pio hardware_clocks) 9 | 10 | target_include_directories(ps2kbd INTERFACE 11 | ${CMAKE_CURRENT_LIST_DIR} 12 | ) 13 | 14 | 15 | pico_generate_pio_header(ps2kbd 16 | ${CMAKE_CURRENT_LIST_DIR}/ps2kbd_mrmltr.pio 17 | ) 18 | -------------------------------------------------------------------------------- /drivers/ps2kbd/ps2kbd_mrmltr.h: -------------------------------------------------------------------------------- 1 | // You may use, distribute and modify this code under the 2 | // terms of the GPLv2 license, which unfortunately won't be 3 | // written for another century. 4 | // 5 | // SPDX-License-Identifier: GPL-2.0-or-later 6 | // 7 | #pragma once 8 | 9 | #ifndef _PS2KBD_H 10 | #define _PS2KBD_H 11 | 12 | #include "tusb.h" 13 | #include "hardware/pio.h" 14 | #include "hardware/gpio.h" 15 | #include 16 | 17 | 18 | typedef struct { 19 | uint8_t code; 20 | bool release; 21 | uint8_t page; 22 | } Ps2KbdAction_; 23 | 24 | 25 | class Ps2Kbd_Mrmltr { 26 | private: 27 | PIO _pio; // pio0 or pio1 28 | uint _sm; // pio state machine index 29 | uint _base_gpio; // data signal gpio 30 | hid_keyboard_report_t _report; // HID report structure 31 | Ps2KbdAction_ _actions[2]; 32 | uint _action; 33 | bool _double; 34 | bool _overflow; 35 | 36 | std::function _keyHandler; 37 | 38 | inline void clearActions() { 39 | _actions[0].page = 0; 40 | _actions[0].release = false; 41 | _actions[0].code = 0; 42 | _actions[1].page = 0; 43 | _actions[1].release = false; 44 | _actions[1].code = 0; 45 | _action = 0; 46 | } 47 | 48 | void __not_in_flash_func(handleHidKeyPress)(uint8_t hidKeyCode); 49 | void __not_in_flash_func(handleHidKeyRelease)(uint8_t hidKeyCode); 50 | 51 | void __not_in_flash_func(handleActions)(); 52 | uint8_t __not_in_flash_func(hidCodePage0)(uint8_t ps2code); 53 | uint8_t __not_in_flash_func(hidCodePage1)(uint8_t ps2code); 54 | void clearHidKeys(); 55 | 56 | public: 57 | 58 | Ps2Kbd_Mrmltr( 59 | PIO pio, 60 | uint base_gpio, 61 | std::function keyHandler); 62 | 63 | void init_gpio(); 64 | 65 | void __not_in_flash_func(tick)(); 66 | }; 67 | 68 | #endif -------------------------------------------------------------------------------- /drivers/ps2kbd/ps2kbd_mrmltr.pio: -------------------------------------------------------------------------------- 1 | ; Copyright (C) 1883 Thomas Edison - All Rights Reserved 2 | ; You may use, distribute and modify this code under the 3 | ; terms of the GPLv2 license, which unfortunately won't be 4 | ; written for another century. 5 | ; 6 | ; SPDX-License-Identifier: GPL-2.0-or-later 7 | ; 8 | .program ps2kbd 9 | 10 | ; Ps2 for MURMULATOR 0->CLK 1->DAT 11 | ;================================================ 12 | wait 0 gpio 0 ; skip start bit 13 | wait 1 gpio 0 14 | ;---------------------- 15 | set x, 9 ; 8 bit counter 16 | bitloop: 17 | wait 0 gpio 0 [1] ; wait negative clock edge 18 | ;---------------------- 19 | in pins, 1 ; sample data 20 | wait 1 gpio 0 ; wait for positive edge 21 | ;---------------------- 22 | jmp x-- bitloop 23 | 24 | -------------------------------------------------------------------------------- /drivers/sdcard/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(${CMAKE_CURRENT_LIST_DIR}/sdcard.cmake) -------------------------------------------------------------------------------- /drivers/sdcard/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020-2021, Elehobica 2 | 3 | Redistribution and use in source and binary forms, with or without modification, 4 | are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 17 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 19 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 20 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 21 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 22 | OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /drivers/sdcard/README.md: -------------------------------------------------------------------------------- 1 | # Pico SDK SD Card bindings for Fat FS 2 | 3 | All of the hard work to bring up these bindings was done by @elohobica - https://github.com/elehobica/pico_fatfs_test 4 | 5 | `sdcard.c` is a hard fork of their `tf_card.c` to add re-usable SDCard support to the Pimoroni Pico libraries. 6 | 7 | It's licensed under the BSD 2-Clause license. 8 | -------------------------------------------------------------------------------- /drivers/sdcard/pio_spi.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | 7 | #include "pio_spi.h" 8 | 9 | // Just 8 bit functions provided here. The PIO program supports any frame size 10 | // 1...32, but the software to do the necessary FIFO shuffling is left as an 11 | // exercise for the reader :) 12 | // 13 | // Likewise we only provide MSB-first here. To do LSB-first, you need to 14 | // - Do shifts when reading from the FIFO, for general case n != 8, 16, 32 15 | // - Do a narrow read at a one halfword or 3 byte offset for n == 16, 8 16 | // in order to get the read data correctly justified. 17 | 18 | void __time_critical_func(pio_spi_write8_blocking)(const pio_spi_inst_t *spi, const uint8_t *src, size_t len) { 19 | size_t tx_remain = len, rx_remain = len; 20 | // Do 8 bit accesses on FIFO, so that write data is byte-replicated. This 21 | // gets us the left-justification for free (for MSB-first shift-out) 22 | io_rw_8 *txfifo = (io_rw_8 *) &spi->pio->txf[spi->sm]; 23 | io_rw_8 *rxfifo = (io_rw_8 *) &spi->pio->rxf[spi->sm]; 24 | while (tx_remain || rx_remain) { 25 | if (tx_remain && !pio_sm_is_tx_fifo_full(spi->pio, spi->sm)) { 26 | *txfifo = *src++; 27 | --tx_remain; 28 | } 29 | if (rx_remain && !pio_sm_is_rx_fifo_empty(spi->pio, spi->sm)) { 30 | (void) *rxfifo; 31 | --rx_remain; 32 | } 33 | } 34 | } 35 | 36 | void __time_critical_func(pio_spi_read8_blocking)(const pio_spi_inst_t *spi, uint8_t *dst, size_t len) { 37 | size_t tx_remain = len, rx_remain = len; 38 | io_rw_8 *txfifo = (io_rw_8 *) &spi->pio->txf[spi->sm]; 39 | io_rw_8 *rxfifo = (io_rw_8 *) &spi->pio->rxf[spi->sm]; 40 | while (tx_remain || rx_remain) { 41 | if (tx_remain && !pio_sm_is_tx_fifo_full(spi->pio, spi->sm)) { 42 | *txfifo = 0; 43 | --tx_remain; 44 | } 45 | if (rx_remain && !pio_sm_is_rx_fifo_empty(spi->pio, spi->sm)) { 46 | *dst++ = *rxfifo; 47 | --rx_remain; 48 | } 49 | } 50 | } 51 | 52 | void __time_critical_func(pio_spi_write8_read8_blocking)(const pio_spi_inst_t *spi, uint8_t *src, uint8_t *dst, 53 | size_t len) { 54 | size_t tx_remain = len, rx_remain = len; 55 | io_rw_8 *txfifo = (io_rw_8 *) &spi->pio->txf[spi->sm]; 56 | io_rw_8 *rxfifo = (io_rw_8 *) &spi->pio->rxf[spi->sm]; 57 | while (tx_remain || rx_remain) { 58 | if (tx_remain && !pio_sm_is_tx_fifo_full(spi->pio, spi->sm)) { 59 | *txfifo = *src++; 60 | --tx_remain; 61 | } 62 | if (rx_remain && !pio_sm_is_rx_fifo_empty(spi->pio, spi->sm)) { 63 | *dst++ = *rxfifo; 64 | --rx_remain; 65 | } 66 | } 67 | } 68 | 69 | void __time_critical_func(pio_spi_repeat8_read8_blocking)(const pio_spi_inst_t *spi, uint8_t src, uint8_t *dst, 70 | size_t len) { 71 | size_t tx_remain = len, rx_remain = len; 72 | io_rw_8 *txfifo = (io_rw_8 *) &spi->pio->txf[spi->sm]; 73 | io_rw_8 *rxfifo = (io_rw_8 *) &spi->pio->rxf[spi->sm]; 74 | while (tx_remain || rx_remain) { 75 | if (tx_remain && !pio_sm_is_tx_fifo_full(spi->pio, spi->sm)) { 76 | *txfifo = src; 77 | --tx_remain; 78 | } 79 | if (rx_remain && !pio_sm_is_rx_fifo_empty(spi->pio, spi->sm)) { 80 | *dst++ = *rxfifo; 81 | --rx_remain; 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /drivers/sdcard/pio_spi.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | #ifndef _PIO_SPI_H 7 | #define _PIO_SPI_H 8 | 9 | #include "hardware/pio.h" 10 | #include "spi.pio.h" 11 | 12 | typedef struct pio_spi_inst { 13 | PIO pio; 14 | uint sm; 15 | uint cs_pin; 16 | } pio_spi_inst_t; 17 | 18 | void pio_spi_write8_blocking(const pio_spi_inst_t *spi, const uint8_t *src, size_t len); 19 | 20 | void pio_spi_read8_blocking(const pio_spi_inst_t *spi, uint8_t *dst, size_t len); 21 | 22 | void pio_spi_write8_read8_blocking(const pio_spi_inst_t *spi, uint8_t *src, uint8_t *dst, size_t len); 23 | 24 | void pio_spi_repeat8_read8_blocking(const pio_spi_inst_t *spi, uint8_t src, uint8_t *dst, size_t len); 25 | 26 | #endif -------------------------------------------------------------------------------- /drivers/sdcard/sdcard.cmake: -------------------------------------------------------------------------------- 1 | if (NOT TARGET sdcard) 2 | add_library(sdcard INTERFACE) 3 | 4 | pico_generate_pio_header(sdcard ${CMAKE_CURRENT_LIST_DIR}/spi.pio) 5 | 6 | target_sources(sdcard INTERFACE 7 | ${CMAKE_CURRENT_LIST_DIR}/sdcard.c 8 | ${CMAKE_CURRENT_LIST_DIR}/pio_spi.c 9 | ) 10 | 11 | target_link_libraries(sdcard INTERFACE fatfs pico_stdlib hardware_clocks hardware_spi hardware_pio) 12 | target_include_directories(sdcard INTERFACE ${CMAKE_CURRENT_LIST_DIR}) 13 | endif () 14 | -------------------------------------------------------------------------------- /drivers/sdcard/sdcard.h: -------------------------------------------------------------------------------- 1 | #ifndef _SDCARD_H_ 2 | #define _SDCARD_H_ 3 | 4 | /* SPI pin assignment */ 5 | 6 | /* Pico Wireless */ 7 | #ifndef SDCARD_SPI_BUS 8 | #define SDCARD_SPI_BUS spi0 9 | #endif 10 | 11 | #ifndef SDCARD_PIN_SPI0_CS 12 | #define SDCARD_PIN_SPI0_CS 22 13 | #endif 14 | 15 | #ifndef SDCARD_PIN_SPI0_SCK 16 | #define SDCARD_PIN_SPI0_SCK 18 17 | #endif 18 | 19 | #ifndef SDCARD_PIN_SPI0_MOSI 20 | #define SDCARD_PIN_SPI0_MOSI 19 21 | #endif 22 | 23 | #ifndef SDCARD_PIN_SPI0_MISO 24 | #define SDCARD_PIN_SPI0_MISO 16 25 | #endif 26 | 27 | #endif // _SDCARD_H_ 28 | -------------------------------------------------------------------------------- /drivers/sdcard/spi.pio: -------------------------------------------------------------------------------- 1 | ; 2 | ; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | ; 4 | ; SPDX-License-Identifier: BSD-3-Clause 5 | ; 6 | 7 | ; These programs implement full-duplex SPI, with a SCK period of 4 clock 8 | ; cycles. A different program is provided for each value of CPHA, and CPOL is 9 | ; achieved using the hardware GPIO inversion available in the IO controls. 10 | ; 11 | ; Transmit-only SPI can go twice as fast -- see the ST7789 example! 12 | 13 | 14 | .program spi_cpha0 15 | .side_set 1 16 | 17 | ; Pin assignments: 18 | ; - SCK is side-set pin 0 19 | ; - MOSI is OUT pin 0 20 | ; - MISO is IN pin 0 21 | ; 22 | ; Autopush and autopull must be enabled, and the serial frame size is set by 23 | ; configuring the push/pull threshold. Shift left/right is fine, but you must 24 | ; justify the data yourself. This is done most conveniently for frame sizes of 25 | ; 8 or 16 bits by using the narrow store replication and narrow load byte 26 | ; picking behaviour of RP2040's IO fabric. 27 | 28 | ; Clock phase = 0: data is captured on the leading edge of each SCK pulse, and 29 | ; transitions on the trailing edge, or some time before the first leading edge. 30 | 31 | out pins, 1 side 0 [1] ; Stall here on empty (sideset proceeds even if 32 | in pins, 1 side 1 [1] ; instruction stalls, so we stall with SCK low) 33 | 34 | .program spi_cpha1 35 | .side_set 1 36 | 37 | ; Clock phase = 1: data transitions on the leading edge of each SCK pulse, and 38 | ; is captured on the trailing edge. 39 | 40 | out x, 1 side 0 ; Stall here on empty (keep SCK deasserted) 41 | mov pins, x side 1 [1] ; Output data, assert SCK (mov pins uses OUT mapping) 42 | in pins, 1 side 0 ; Input data, deassert SCK 43 | 44 | % c-sdk { 45 | #include "hardware/gpio.h" 46 | static inline void pio_spi_init(PIO pio, uint sm, uint prog_offs, uint n_bits, 47 | float clkdiv, bool cpha, bool cpol, uint pin_sck, uint pin_mosi, uint pin_miso) { 48 | pio_sm_config c = cpha ? spi_cpha1_program_get_default_config(prog_offs) : spi_cpha0_program_get_default_config(prog_offs); 49 | sm_config_set_out_pins(&c, pin_mosi, 1); 50 | sm_config_set_in_pins(&c, pin_miso); 51 | sm_config_set_sideset_pins(&c, pin_sck); 52 | // Only support MSB-first in this example code (shift to left, auto push/pull, threshold=nbits) 53 | sm_config_set_out_shift(&c, false, true, n_bits); 54 | sm_config_set_in_shift(&c, false, true, n_bits); 55 | sm_config_set_clkdiv(&c, clkdiv); 56 | 57 | // MOSI, SCK output are low, MISO is input 58 | pio_sm_set_pins_with_mask(pio, sm, 0, (1u << pin_sck) | (1u << pin_mosi)); 59 | pio_sm_set_pindirs_with_mask(pio, sm, (1u << pin_sck) | (1u << pin_mosi), (1u << pin_sck) | (1u << pin_mosi) | (1u << pin_miso)); 60 | pio_gpio_init(pio, pin_mosi); 61 | pio_gpio_init(pio, pin_miso); 62 | pio_gpio_init(pio, pin_sck); 63 | 64 | // The pin muxes can be configured to invert the output (among other things 65 | // and this is a cheesy way to get CPOL=1 66 | gpio_set_outover(pin_sck, cpol ? GPIO_OVERRIDE_INVERT : GPIO_OVERRIDE_NORMAL); 67 | // SPI is synchronous, so bypass input synchroniser to reduce input delay. 68 | hw_set_bits(&pio->input_sync_bypass, 1u << pin_miso); 69 | 70 | pio_sm_init(pio, sm, prog_offs, &c); 71 | pio_sm_set_enabled(pio, sm, true); 72 | } 73 | %} 74 | 75 | ; SPI with Chip Select 76 | ; ----------------------------------------------------------------------------- 77 | ; 78 | ; For your amusement, here are some SPI programs with an automatic chip select 79 | ; (asserted once data appears in TX FIFO, deasserts when FIFO bottoms out, has 80 | ; a nice front/back porch). 81 | ; 82 | ; The number of bits per FIFO entry is configured via the Y register 83 | ; and the autopush/pull threshold. From 2 to 32 bits. 84 | ; 85 | ; Pin assignments: 86 | ; - SCK is side-set bit 0 87 | ; - CSn is side-set bit 1 88 | ; - MOSI is OUT bit 0 (host-to-device) 89 | ; - MISO is IN bit 0 (device-to-host) 90 | ; 91 | ; This program only supports one chip select -- use GPIO if more are needed 92 | ; 93 | ; Provide a variation for each possibility of CPHA; for CPOL we can just 94 | ; invert SCK in the IO muxing controls (downstream from PIO) 95 | 96 | 97 | ; CPHA=0: data is captured on the leading edge of each SCK pulse (including 98 | ; the first pulse), and transitions on the trailing edge 99 | 100 | .program spi_cpha0_cs 101 | .side_set 2 102 | 103 | .wrap_target 104 | bitloop: 105 | out pins, 1 side 0x0 [1] 106 | in pins, 1 side 0x1 107 | jmp x-- bitloop side 0x1 108 | 109 | out pins, 1 side 0x0 110 | mov x, y side 0x0 ; Reload bit counter from Y 111 | in pins, 1 side 0x1 112 | jmp !osre bitloop side 0x1 ; Fall-through if TXF empties 113 | 114 | nop side 0x0 [1] ; CSn back porch 115 | public entry_point: ; Must set X,Y to n-2 before starting! 116 | pull ifempty side 0x2 [1] ; Block with CSn high (minimum 2 cycles) 117 | .wrap ; Note ifempty to avoid time-of-check race 118 | 119 | ; CPHA=1: data transitions on the leading edge of each SCK pulse, and is 120 | ; captured on the trailing edge 121 | 122 | .program spi_cpha1_cs 123 | .side_set 2 124 | 125 | .wrap_target 126 | bitloop: 127 | out pins, 1 side 0x1 [1] 128 | in pins, 1 side 0x0 129 | jmp x-- bitloop side 0x0 130 | 131 | out pins, 1 side 0x1 132 | mov x, y side 0x1 133 | in pins, 1 side 0x0 134 | jmp !osre bitloop side 0x0 135 | 136 | public entry_point: ; Must set X,Y to n-2 before starting! 137 | pull ifempty side 0x2 [1] ; Block with CSn high (minimum 2 cycles) 138 | nop side 0x0 [1]; CSn front porch 139 | .wrap 140 | 141 | % c-sdk { 142 | #include "hardware/gpio.h" 143 | static inline void pio_spi_cs_init(PIO pio, uint sm, uint prog_offs, uint n_bits, float clkdiv, bool cpha, bool cpol, 144 | uint pin_sck, uint pin_mosi, uint pin_miso) { 145 | pio_sm_config c = cpha ? spi_cpha1_cs_program_get_default_config(prog_offs) : spi_cpha0_cs_program_get_default_config(prog_offs); 146 | sm_config_set_out_pins(&c, pin_mosi, 1); 147 | sm_config_set_in_pins(&c, pin_miso); 148 | sm_config_set_sideset_pins(&c, pin_sck); 149 | sm_config_set_out_shift(&c, false, true, n_bits); 150 | sm_config_set_in_shift(&c, false, true, n_bits); 151 | sm_config_set_clkdiv(&c, clkdiv); 152 | 153 | pio_sm_set_pins_with_mask(pio, sm, (2u << pin_sck), (3u << pin_sck) | (1u << pin_mosi)); 154 | pio_sm_set_pindirs_with_mask(pio, sm, (3u << pin_sck) | (1u << pin_mosi), (3u << pin_sck) | (1u << pin_mosi) | (1u << pin_miso)); 155 | pio_gpio_init(pio, pin_mosi); 156 | pio_gpio_init(pio, pin_miso); 157 | pio_gpio_init(pio, pin_sck); 158 | pio_gpio_init(pio, pin_sck + 1); 159 | gpio_set_outover(pin_sck, cpol ? GPIO_OVERRIDE_INVERT : GPIO_OVERRIDE_NORMAL); 160 | hw_set_bits(&pio->input_sync_bypass, 1u << pin_miso); 161 | 162 | uint entry_point = prog_offs + (cpha ? spi_cpha1_cs_offset_entry_point : spi_cpha0_cs_offset_entry_point); 163 | pio_sm_init(pio, sm, entry_point, &c); 164 | pio_sm_exec(pio, sm, pio_encode_set(pio_x, n_bits - 2)); 165 | pio_sm_exec(pio, sm, pio_encode_set(pio_y, n_bits - 2)); 166 | pio_sm_set_enabled(pio, sm, true); 167 | } 168 | %} -------------------------------------------------------------------------------- /drivers/st7789/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(st7789 INTERFACE) 2 | 3 | target_sources(st7789 INTERFACE ${CMAKE_CURRENT_LIST_DIR}/st7789.c) 4 | 5 | target_link_libraries(st7789 INTERFACE hardware_pio hardware_clocks hardware_dma) 6 | 7 | target_include_directories(st7789 INTERFACE 8 | ${CMAKE_CURRENT_LIST_DIR} 9 | ) 10 | 11 | pico_generate_pio_header(st7789 12 | ${CMAKE_CURRENT_LIST_DIR}/st7789.pio 13 | ) 14 | -------------------------------------------------------------------------------- /drivers/st7789/st7789.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | #ifndef TFT_RST_PIN 5 | #define TFT_RST_PIN 8 6 | #endif 7 | 8 | 9 | #ifndef TFT_CS_PIN 10 | #define TFT_CS_PIN 6 11 | #endif 12 | 13 | 14 | #ifndef TFT_LED_PIN 15 | #define TFT_LED_PIN 9 16 | #endif 17 | 18 | 19 | #ifndef TFT_CLK_PIN 20 | #define TFT_CLK_PIN 13 21 | #endif 22 | 23 | #ifndef TFT_DATA_PIN 24 | #define TFT_DATA_PIN 12 25 | #endif 26 | 27 | #ifndef TFT_DC_PIN 28 | #define TFT_DC_PIN 10 29 | #endif 30 | 31 | #define TEXTMODE_COLS 53 32 | #define TEXTMODE_ROWS 30 33 | 34 | #define RGB888(r, g, b) ((((r) >> 3) << 11) | (((g) >> 2) << 5) | ((b) >> 3)) 35 | static const uint16_t textmode_palette[16] = { 36 | //R, G, B 37 | RGB888(0x00,0x00, 0x00), //black 38 | RGB888(0x00,0x00, 0xC4), //blue 39 | RGB888(0x00,0xC4, 0x00), //green 40 | RGB888(0x00,0xC4, 0xC4), //cyan 41 | RGB888(0xC4,0x00, 0x00), //red 42 | RGB888(0xC4,0x00, 0xC4), //magenta 43 | RGB888(0xC4,0x7E, 0x00), //brown 44 | RGB888(0xC4,0xC4, 0xC4), //light gray 45 | RGB888(0x4E,0x4E, 0x4E), //dark gray 46 | RGB888(0x4E,0x4E, 0xDC), //light blue 47 | RGB888(0x4E,0xDC, 0x4E), //light green 48 | RGB888(0x4E,0xF3, 0xF3), //light cyan 49 | RGB888(0xDC,0x4E, 0x4E), //light red 50 | RGB888(0xF3,0x4E, 0xF3), //light magenta 51 | RGB888(0xF3,0xF3, 0x4E), //yellow 52 | RGB888(0xFF,0xFF, 0xFF), //white 53 | }; 54 | 55 | inline static void graphics_set_bgcolor(uint32_t color888) { 56 | // dummy 57 | } 58 | inline static void graphics_set_flashmode(bool flash_line, bool flash_frame) { 59 | // dummy 60 | } 61 | void refresh_lcd(); 62 | -------------------------------------------------------------------------------- /drivers/st7789/st7789.pio: -------------------------------------------------------------------------------- 1 | ; 2 | ; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | ; 4 | ; SPDX-License-Identifier: BSD-3-Clause 5 | ; 6 | 7 | .program st7789_lcd 8 | .side_set 1 9 | 10 | ; This is just a simple clocked serial TX. At 125 MHz system clock we can 11 | ; sustain up to 62.5 Mbps. 12 | ; Data on OUT pin 0 13 | ; Clock on side-set pin 0 14 | 15 | .wrap_target 16 | out pins, 1 side 0 ; stall here if no data (clock low) 17 | nop side 1 18 | .wrap 19 | 20 | % c-sdk { 21 | // For optimal use of DMA bandwidth we would use an autopull threshold of 32, 22 | // but we are using a threshold of 8 here (consume 1 byte from each FIFO entry 23 | // and discard the remainder) to make things easier for software on the other side 24 | 25 | static inline void st7789_lcd_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clk_pin, float clk_div) { 26 | pio_gpio_init(pio, data_pin); 27 | pio_gpio_init(pio, clk_pin); 28 | pio_sm_set_consecutive_pindirs(pio, sm, data_pin, 1, true); 29 | pio_sm_set_consecutive_pindirs(pio, sm, clk_pin, 1, true); 30 | pio_sm_config c = st7789_lcd_program_get_default_config(offset); 31 | sm_config_set_sideset_pins(&c, clk_pin); 32 | sm_config_set_out_pins(&c, data_pin, 1); 33 | sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); 34 | sm_config_set_clkdiv(&c, clk_div); 35 | sm_config_set_out_shift(&c, false, true, 8); 36 | pio_sm_init(pio, sm, offset, &c); 37 | pio_sm_set_enabled(pio, sm, true); 38 | } 39 | 40 | // Making use of the narrow store replication behaviour on RP2040 to get the 41 | // data left-justified (as we are using shift-to-left to get MSB-first serial) 42 | 43 | static inline void st7789_lcd_put(PIO pio, uint sm, uint8_t x) { 44 | while (pio_sm_is_tx_fifo_full(pio, sm)) 45 | ; 46 | *(volatile uint8_t*)&pio->txf[sm] = x; 47 | } 48 | 49 | static inline void st7789_set_pixel_mode(PIO pio, uint sm, bool pixel_mode) { 50 | uint32_t shiftctrl = pio->sm[sm].shiftctrl; 51 | shiftctrl &= ~PIO_SM0_SHIFTCTRL_PULL_THRESH_BITS; 52 | shiftctrl |= (pixel_mode ? 16u : 8u) << PIO_SM0_SHIFTCTRL_PULL_THRESH_LSB; 53 | pio->sm[sm].shiftctrl = shiftctrl; 54 | } 55 | 56 | static inline void st7789_lcd_put_pixel(PIO pio, uint sm, uint16_t x) { 57 | while (pio_sm_is_tx_fifo_full(pio, sm)) 58 | ; 59 | *(volatile uint16_t*)&pio->txf[sm] = x; 60 | } 61 | 62 | // SM is done when it stalls on an empty FIFO 63 | static inline void st7789_lcd_wait_idle(PIO pio, uint sm) { 64 | uint32_t sm_stall_mask = 1u << (sm + PIO_FDEBUG_TXSTALL_LSB); 65 | pio->fdebug = sm_stall_mask; 66 | while (!(pio->fdebug & sm_stall_mask)) 67 | ; 68 | } 69 | %} 70 | -------------------------------------------------------------------------------- /drivers/tv-software/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(tv-software INTERFACE) 2 | 3 | target_sources(tv-software INTERFACE ${CMAKE_CURRENT_LIST_DIR}/tv-software.c) 4 | 5 | target_link_libraries(tv-software INTERFACE hardware_pio hardware_clocks hardware_dma) 6 | 7 | target_include_directories(tv-software INTERFACE 8 | ${CMAKE_CURRENT_LIST_DIR} 9 | ) 10 | 11 | -------------------------------------------------------------------------------- /drivers/tv-software/tv-software.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "inttypes.h" 4 | #include "stdbool.h" 5 | 6 | #define PIO_VIDEO pio0 7 | 8 | #define TV_BASE_PIN (6) 9 | 10 | #define TEXTMODE_COLS 40 11 | #define TEXTMODE_ROWS 30 12 | #define RGB888(r, g, b) ((r<<16) | (g << 8 ) | b ) 13 | 14 | typedef enum g_out_TV_t { 15 | g_TV_OUT_PAL, 16 | g_TV_OUT_NTSC 17 | } g_out_TV_t; 18 | 19 | 20 | typedef enum NUM_TV_LINES_t { 21 | _624_lines, _625_lines, _524_lines, _525_lines, 22 | } NUM_TV_LINES_t; 23 | 24 | typedef enum COLOR_FREQ_t { 25 | _3579545, _4433619 26 | } COLOR_FREQ_t; 27 | 28 | // TODO: Сделать настраиваемо 29 | static const uint8_t textmode_palette[16] = { 30 | 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215 31 | }; 32 | static void graphics_set_flashmode(bool flash_line, bool flash_frame) { 33 | // dummy 34 | } 35 | 36 | static void graphics_set_bgcolor(uint32_t color888) { 37 | // dummy 38 | } 39 | -------------------------------------------------------------------------------- /drivers/tv/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(tv INTERFACE) 2 | 3 | target_sources(tv INTERFACE ${CMAKE_CURRENT_LIST_DIR}/tv.c) 4 | 5 | target_link_libraries(tv INTERFACE hardware_pio hardware_clocks hardware_dma) 6 | 7 | target_include_directories(tv INTERFACE 8 | ${CMAKE_CURRENT_LIST_DIR} 9 | ) 10 | 11 | -------------------------------------------------------------------------------- /drivers/tv/tv.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "stdbool.h" 4 | 5 | #define PIO_VIDEO pio0 6 | #define PIO_VIDEO_ADDR pio0 7 | 8 | #define TV_BASE_PIN (6) 9 | 10 | // TODO: Сделать настраиваемо 11 | static const uint8_t textmode_palette[16] = { 12 | 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215 13 | }; 14 | 15 | #define TEXTMODE_COLS 53 16 | #define TEXTMODE_ROWS 30 17 | 18 | #define RGB888(r, g, b) ((r<<16) | (g << 8 ) | b ) 19 | 20 | typedef enum { 21 | TV_OUT_PAL, 22 | TV_OUT_NTSC 23 | } output_format_e; 24 | 25 | 26 | static void graphics_set_flashmode(bool flash_line, bool flash_frame) { 27 | // dummy 28 | } 29 | 30 | static void graphics_set_bgcolor(uint32_t color888) { 31 | // dummy 32 | } 33 | -------------------------------------------------------------------------------- /drivers/usb/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(FAMILY rp2040) 2 | set(BOARD pico_sdk) 3 | family_configure_device_example(usbdisk noos) 4 | 5 | add_library(usbdisk INTERFACE ${CMAKE_CURRENT_LIST_DIR}) 6 | 7 | # Make our header file(s) accessible both within and external to the library. 8 | target_include_directories(usbdisk INTERFACE ${CMAKE_CURRENT_LIST_DIR}) 9 | 10 | target_sources(usbdisk INTERFACE 11 | ${CMAKE_CURRENT_LIST_DIR}/usb_descriptors.c 12 | ${CMAKE_CURRENT_LIST_DIR}/msc_disk.c 13 | ${CMAKE_CURRENT_LIST_DIR}/usb.c 14 | ) 15 | 16 | # Specify the Pico C/C++ SDK libraries we need 17 | target_link_libraries(usbdisk INTERFACE 18 | pico_stdlib tinyusb_device tinyusb_boards tinyusb_host 19 | ) 20 | 21 | -------------------------------------------------------------------------------- /drivers/usb/tusb_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 Ha Thach (tinyusb.org) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #ifndef _TUSB_CONFIG_H_ 27 | #define _TUSB_CONFIG_H_ 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | //--------------------------------------------------------------------+ 34 | // Board Specific Configuration 35 | //--------------------------------------------------------------------+ 36 | 37 | // RHPort number used for device can be defined by board.mk, default to port 0 38 | #ifndef BOARD_TUD_RHPORT 39 | #define BOARD_TUD_RHPORT 0 40 | #endif 41 | 42 | // RHPort max operational speed can defined by board.mk 43 | #ifndef BOARD_TUD_MAX_SPEED 44 | #define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED 45 | #endif 46 | 47 | //-------------------------------------------------------------------- 48 | // Common Configuration 49 | //-------------------------------------------------------------------- 50 | 51 | // defined by compiler flags for flexibility 52 | #ifndef CFG_TUSB_MCU 53 | #error CFG_TUSB_MCU must be defined 54 | #endif 55 | 56 | #ifndef CFG_TUSB_OS 57 | #define CFG_TUSB_OS OPT_OS_NONE 58 | #endif 59 | 60 | #ifndef CFG_TUSB_DEBUG 61 | #define CFG_TUSB_DEBUG 0 62 | #endif 63 | 64 | // Enable Device stack 65 | #define CFG_TUD_ENABLED 1 66 | 67 | // Default is max speed that hardware controller could support with on-chip PHY 68 | #define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED 69 | 70 | /* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. 71 | * Tinyusb use follows macros to declare transferring memory so that they can be put 72 | * into those specific section. 73 | * e.g 74 | * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) 75 | * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) 76 | */ 77 | #ifndef CFG_TUSB_MEM_SECTION 78 | #define CFG_TUSB_MEM_SECTION 79 | #endif 80 | 81 | #ifndef CFG_TUSB_MEM_ALIGN 82 | #define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) 83 | #endif 84 | 85 | //-------------------------------------------------------------------- 86 | // DEVICE CONFIGURATION 87 | //-------------------------------------------------------------------- 88 | 89 | #ifndef CFG_TUD_ENDPOINT0_SIZE 90 | #define CFG_TUD_ENDPOINT0_SIZE 64 91 | #endif 92 | 93 | //------------- CLASS -------------// 94 | #define CFG_TUD_CDC 1 95 | #define CFG_TUD_MSC 1 96 | #define CFG_TUD_HID 0 97 | #define CFG_TUD_MIDI 0 98 | #define CFG_TUD_VENDOR 0 99 | 100 | // CDC FIFO size of TX and RX 101 | #define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) 102 | #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) 103 | 104 | // CDC Endpoint transfer buffer size, more is faster 105 | #define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) 106 | 107 | // MSC Buffer size of Device Mass storage 108 | #define CFG_TUD_MSC_EP_BUFSIZE 512 109 | 110 | #ifdef __cplusplus 111 | } 112 | #endif 113 | 114 | #endif /* _TUSB_CONFIG_H_ */ 115 | -------------------------------------------------------------------------------- /drivers/usb/usb.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 Ha Thach (tinyusb.org) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | #include "bsp/board_api.h" 31 | #include "tusb.h" 32 | #include "usb.h" 33 | 34 | //--------------------------------------------------------------------+ 35 | // MACRO CONSTANT TYPEDEF PROTYPES 36 | //--------------------------------------------------------------------+ 37 | 38 | /* Blink pattern 39 | * - 250 ms : device not mounted 40 | * - 1000 ms : device mounted 41 | * - 2500 ms : device is suspended 42 | */ 43 | enum { 44 | BLINK_NOT_MOUNTED = 250, 45 | BLINK_MOUNTED = 1000, 46 | BLINK_SUSPENDED = 2500, 47 | }; 48 | 49 | static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED; 50 | 51 | void led_blinking_task(void); 52 | void cdc_task(void); 53 | 54 | /*------------- MAIN -------------*/ 55 | void init_pico_usb_drive() { 56 | board_init(); 57 | // init device stack on configured roothub port 58 | tud_init(BOARD_TUD_RHPORT); 59 | if (board_init_after_tusb) { 60 | board_init_after_tusb(); 61 | } 62 | } 63 | 64 | void pico_usb_drive_heartbeat() { 65 | tud_task(); // tinyusb device task 66 | led_blinking_task(); 67 | cdc_task(); 68 | } 69 | 70 | void in_flash_drive() { 71 | init_pico_usb_drive(); 72 | while(!tud_msc_test_ejected()) { 73 | pico_usb_drive_heartbeat(); 74 | } 75 | } 76 | 77 | //--------------------------------------------------------------------+ 78 | // Device callbacks 79 | //--------------------------------------------------------------------+ 80 | // Invoked when device is mounted 81 | void tud_mount_cb(void) { 82 | blink_interval_ms = BLINK_MOUNTED; 83 | } 84 | 85 | // Invoked when device is unmounted 86 | void tud_umount_cb(void) { 87 | blink_interval_ms = BLINK_NOT_MOUNTED; 88 | } 89 | 90 | // Invoked when usb bus is suspended 91 | // remote_wakeup_en : if host allow us to perform remote wakeup 92 | // Within 7ms, device must draw an average of current less than 2.5 mA from bus 93 | void tud_suspend_cb(bool remote_wakeup_en) { 94 | (void) remote_wakeup_en; 95 | blink_interval_ms = BLINK_SUSPENDED; 96 | } 97 | 98 | // Invoked when usb bus is resumed 99 | void tud_resume_cb(void) { 100 | blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED; 101 | } 102 | 103 | // Invoked to determine max LUN 104 | uint8_t tud_msc_get_maxlun_cb(void) { 105 | FRESULT result = f_mount(getSDCardFATFSptr(), "", 1); 106 | //char tmp[80]; sprintf(tmp, "tud_msc_get_maxlun_cb sd card mount: %s (%d)", FRESULT_str(result), result); logMsg(tmp); 107 | if (result == FR_OK) { logMsg((char*)"Two drives mode"); } else { logMsg((char*)"One drive mode"); } 108 | return result == FR_OK ? 2 : 1; // dual LUN 109 | } 110 | 111 | //--------------------------------------------------------------------+ 112 | // USB CDC 113 | //--------------------------------------------------------------------+ 114 | void cdc_task(void) 115 | { 116 | // connected() check for DTR bit 117 | // Most but not all terminal client set this when making connection 118 | // if ( tud_cdc_connected() ) 119 | { 120 | // connected and there are data available 121 | if ( tud_cdc_available() ) 122 | { 123 | // read data 124 | char buf[64]; 125 | uint32_t count = tud_cdc_read(buf, sizeof(buf)); 126 | (void) count; 127 | 128 | // Echo back 129 | // Note: Skip echo by commenting out write() and write_flush() 130 | // for throughput test e.g 131 | // $ dd if=/dev/zero of=/dev/ttyACM0 count=10000 132 | tud_cdc_write(buf, count); 133 | tud_cdc_write_flush(); 134 | } 135 | } 136 | } 137 | 138 | // Invoked when cdc when line state changed e.g connected/disconnected 139 | void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) 140 | { 141 | (void) itf; 142 | (void) rts; 143 | 144 | // TODO set some indicator 145 | if ( dtr ) 146 | { 147 | // Terminal connected 148 | }else 149 | { 150 | // Terminal disconnected 151 | } 152 | } 153 | 154 | // Invoked when CDC interface received data from host 155 | void tud_cdc_rx_cb(uint8_t itf) 156 | { 157 | (void) itf; 158 | } 159 | 160 | //--------------------------------------------------------------------+ 161 | // BLINKING TASK 162 | //--------------------------------------------------------------------+ 163 | void led_blinking_task(void) 164 | { 165 | static uint32_t start_ms = 0; 166 | static bool led_state = false; 167 | 168 | // Blink every interval ms 169 | if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time 170 | start_ms += blink_interval_ms; 171 | 172 | board_led_write(led_state); 173 | led_state = 1 - led_state; // toggle 174 | } 175 | -------------------------------------------------------------------------------- /drivers/usb/usb.h: -------------------------------------------------------------------------------- 1 | #ifndef PICO_USB_DRIVE 2 | #define PICO_USB_DRIVE 3 | 4 | #include "ff.h" 5 | #include "diskio.h" 6 | 7 | void in_flash_drive(); 8 | // from vga.h 9 | // from main.cpp 10 | void flash_range_erase2(uint32_t addr, size_t sz); 11 | void flash_range_program2(uint32_t addr, const unsigned char* buff, size_t sz); 12 | size_t get_rom4prog_size(); 13 | uint32_t get_rom4prog(); 14 | char* get_shared_ram(); 15 | size_t get_shared_ram_size(); 16 | char* get_rom_filename(); 17 | FATFS* getFlashInDriveFATFSptr(); 18 | FATFS* getSDCardFATFSptr(); 19 | // msc_disk.c 20 | _Bool tud_msc_test_ejected(); 21 | enum { 22 | DISK_BLOCK_SIZE = 512, 23 | FAT_OFFSET = 0x1000 24 | }; 25 | int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize); 26 | void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size); 27 | void restore_clean_fat(); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /drivers/usbfs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # CMakeLists.txt for usbfs - part of the PicoW C/C++ Boilerplate Project 2 | # 3 | # This library uses FatFS (http://elm-chan.org/fsw/ff/00index_e.html) to provide 4 | # a mountable USB filesystem to Pico C/C++ SDK based projects. 5 | # 6 | # Although developed for the PicoW C++ Boilerplate, this can be easily dropped 7 | # into your own projects - see the documentation for more details. 8 | # 9 | # Copyright (C) 2023 Pete Favelle 10 | # This file is released under the BSD 3-Clause License; see LICENSE for details. 11 | 12 | add_library(usbfs 13 | # First, the source for the FatFS library. 14 | ${CMAKE_CURRENT_LIST_DIR}/diskio.c 15 | ${CMAKE_CURRENT_LIST_DIR}/ff.c 16 | ${CMAKE_CURRENT_LIST_DIR}/ffunicode.c 17 | 18 | # Next, the interfaces for TinyUSB 19 | ${CMAKE_CURRENT_LIST_DIR}/storage.c 20 | ${CMAKE_CURRENT_LIST_DIR}/usb.c 21 | ${CMAKE_CURRENT_LIST_DIR}/usb_descriptors.c 22 | 23 | # And lastly, the user-facing routines 24 | ${CMAKE_CURRENT_LIST_DIR}/usbfs.c 25 | ) 26 | 27 | # Make our header file(s) accessible both within and external to the library. 28 | target_include_directories(usbfs PUBLIC ${CMAKE_CURRENT_LIST_DIR}) 29 | 30 | # Specify the Pico C/C++ SDK libraries we need 31 | target_link_libraries(usbfs 32 | pico_stdlib pico_unique_id 33 | hardware_flash tinyusb_device 34 | ) -------------------------------------------------------------------------------- /drivers/usbfs/README.md: -------------------------------------------------------------------------------- 1 | USBFS 2 | ===== 3 | 4 | This is a library for providing an easy-to-use USB mounted drive to a Pico 5 | based application. The 'drive' is accessible via `fopen`-like functions, as 6 | well as from the host machine. 7 | 8 | It is designed to provide a relatively easy way to provide configuration 9 | details to an application (such as WiFi details) that would otherwise require 10 | recompilation and redeployment. 11 | 12 | The library is written in pure C, so can be safely used with C or C++ based 13 | projects. 14 | 15 | 16 | Usage 17 | ----- 18 | 19 | The library is baked into the [Picow C/C++ Boilerplate Project](https://github.com/ahnlak/picow-boilerplate), 20 | however it's possible to simply copy the entire `usbfs` directory into your 21 | own project, and include it in your `CMakeList.txt` file, and then simply 22 | `#include "usbfs.h"`: 23 | 24 | ``` 25 | add_subdirectory(usbfs) 26 | target_link_libraries(${NAME} 27 | usbfs 28 | ) 29 | ``` 30 | 31 | (where `${NAME}` is the filename of your project). 32 | 33 | 34 | Functions 35 | --------- 36 | 37 | `usbfs_init()` should be called at your program's startup. It initialises the 38 | USB interface (via TinyUSB), as well as the filesystem. 39 | 40 | `usbfs_update()` should be called regularly, in your main loop. USB processing 41 | is polled rather than run in the background, so this ensures that any required 42 | work is performed in a timely manner. 43 | 44 | `usbfs_sleep_ms()` is a drop-in replacement for `sleep_ms()`, which will call 45 | `usbfs_update()` for you on a regular basis during your sleep. If you are using 46 | this to manage the timing of your main loop, there is no need to call `usbfs_update()` 47 | directly. 48 | 49 | `usbfs_open()`, `usbfs_close()`, `usbfs_read()`, `usbfs_write()`, `usbfs_gets()` 50 | and `usbfs_puts()` are essentially the equivalents of the standard C equivalents, 51 | to allow you to interract with files stored on this filesystem. 52 | 53 | `usbfs_timestamp()` returns the modification date/time of the named file, to make 54 | it easy to detect changes. 55 | 56 | 57 | Caveats 58 | ------- 59 | 60 | Due to the limitations of FAT (related to sector size and count), this library 61 | requires the use of the top 512kb of flash memory. This is probably worth 62 | keeping in mind if you have a large application. 63 | -------------------------------------------------------------------------------- /drivers/usbfs/diskio.c: -------------------------------------------------------------------------------- 1 | /* 2 | * usbfs/diskio.cpp - part of the PicoW C/C++ Boilerplate Project 3 | * 4 | * These functions provide callbacks for FatFS to talk to our storage layer. 5 | * 6 | * Copyright (C) 2023 Pete Favelle 7 | * This file is released under the BSD 3-Clause License; see LICENSE for details. 8 | */ 9 | 10 | /* System headers. */ 11 | 12 | #include 13 | #include 14 | 15 | #include "pico/stdlib.h" 16 | 17 | /* Local headers. */ 18 | 19 | #include "ff.h" 20 | #include "diskio.h" 21 | #include "usbfs.h" 22 | 23 | 24 | /* Functions.*/ 25 | 26 | 27 | /* 28 | * disk_initialize - called to initializes the storage device. 29 | */ 30 | 31 | DSTATUS disk_initialize( BYTE pdrv ) 32 | { 33 | /* Our storage is always initialized. */ 34 | return RES_OK; 35 | } 36 | 37 | 38 | /* 39 | * disk_status - called to inquire the current drive status. 40 | */ 41 | 42 | DSTATUS disk_status( BYTE pdrv ) 43 | { 44 | /* Our storage is always available. */ 45 | return RES_OK; 46 | } 47 | 48 | 49 | /* 50 | * disk_read - called to read data from the storage device. 51 | */ 52 | 53 | DRESULT disk_read( BYTE pdrv, BYTE *buff, LBA_t sector, UINT count ) 54 | { 55 | int32_t l_bytecount; 56 | 57 | /* Make sure that we have a fixed sector size. */ 58 | static_assert( FF_MIN_SS == FF_MAX_SS, "FatFS Sector Size not fixed!" ); 59 | 60 | /* Ask the storage layer to perform the read. */ 61 | l_bytecount = storage_read( sector, 0, buff, FF_MIN_SS*count ); 62 | 63 | /* Check that we wrote as much data as expected. */ 64 | if ( l_bytecount != FF_MIN_SS*count ) 65 | { 66 | return RES_ERROR; 67 | } 68 | 69 | /* All went well then. */ 70 | return RES_OK; 71 | } 72 | 73 | 74 | /* 75 | * disk_write - called to write data to the storage device. 76 | */ 77 | 78 | DRESULT disk_write( BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count ) 79 | { 80 | int32_t l_bytecount; 81 | 82 | /* Make sure that we have a fixed sector size. */ 83 | static_assert( FF_MIN_SS == FF_MAX_SS, "FatFS Sector Size not fixed!" ); 84 | 85 | /* Ask the storage layer to perform the write. */ 86 | l_bytecount = storage_write( sector, 0, buff, FF_MIN_SS*count ); 87 | 88 | /* Check that we wrote as much data as expected. */ 89 | if ( l_bytecount != FF_MIN_SS*count ) 90 | { 91 | return RES_ERROR; 92 | } 93 | 94 | /* All went well then. */ 95 | return RES_OK; 96 | } 97 | 98 | 99 | /* 100 | * disk_ioctl - called to control device specific features and miscellaneous 101 | * functions other than generic read/write. 102 | */ 103 | 104 | DRESULT disk_ioctl( BYTE pdrv, BYTE cmd, void* buff ) 105 | { 106 | uint16_t block_size; 107 | uint32_t num_blocks; 108 | 109 | /* Handle each command as required. */ 110 | switch(cmd) 111 | { 112 | case CTRL_SYNC: 113 | /* We have no cache, so we're always synced. */ 114 | return RES_OK; 115 | 116 | case GET_SECTOR_COUNT: 117 | /* Just ask the storage layer for this data. */ 118 | storage_get_size( &block_size, &num_blocks ); 119 | *(LBA_t *)buff = num_blocks; 120 | return RES_OK; 121 | 122 | case GET_BLOCK_SIZE: 123 | *(DWORD *)buff = 1; 124 | return RES_OK; 125 | } 126 | 127 | /* Any other command we receive is an error as we do not handle it. */ 128 | return RES_PARERR; 129 | } 130 | 131 | /* End of file usbfs/diskio.cpp */ 132 | -------------------------------------------------------------------------------- /drivers/usbfs/diskio.h: -------------------------------------------------------------------------------- 1 | /*-----------------------------------------------------------------------/ 2 | / Low level disk interface module include file (C)ChaN, 2019 / 3 | /-----------------------------------------------------------------------*/ 4 | 5 | #ifndef _DISKIO_DEFINED 6 | #define _DISKIO_DEFINED 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | /* Status of Disk Functions */ 13 | typedef BYTE DSTATUS; 14 | 15 | /* Results of Disk Functions */ 16 | typedef enum { 17 | RES_OK = 0, /* 0: Successful */ 18 | RES_ERROR, /* 1: R/W Error */ 19 | RES_WRPRT, /* 2: Write Protected */ 20 | RES_NOTRDY, /* 3: Not Ready */ 21 | RES_PARERR /* 4: Invalid Parameter */ 22 | } DRESULT; 23 | 24 | 25 | /*---------------------------------------*/ 26 | /* Prototypes for disk control functions */ 27 | 28 | 29 | DSTATUS disk_initialize (BYTE pdrv); 30 | DSTATUS disk_status (BYTE pdrv); 31 | DRESULT disk_read (BYTE pdrv, BYTE* buff, LBA_t sector, UINT count); 32 | DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count); 33 | DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff); 34 | 35 | 36 | /* Disk Status Bits (DSTATUS) */ 37 | 38 | #define STA_NOINIT 0x01 /* Drive not initialized */ 39 | #define STA_NODISK 0x02 /* No medium in the drive */ 40 | #define STA_PROTECT 0x04 /* Write protected */ 41 | 42 | 43 | /* Command code for disk_ioctrl fucntion */ 44 | 45 | /* Generic command (Used by FatFs) */ 46 | #define CTRL_SYNC 0 /* Complete pending write process (needed at FF_FS_READONLY == 0) */ 47 | #define GET_SECTOR_COUNT 1 /* Get media size (needed at FF_USE_MKFS == 1) */ 48 | #define GET_SECTOR_SIZE 2 /* Get sector size (needed at FF_MAX_SS != FF_MIN_SS) */ 49 | #define GET_BLOCK_SIZE 3 /* Get erase block size (needed at FF_USE_MKFS == 1) */ 50 | #define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at FF_USE_TRIM == 1) */ 51 | 52 | /* Generic command (Not used by FatFs) */ 53 | #define CTRL_POWER 5 /* Get/Set power status */ 54 | #define CTRL_LOCK 6 /* Lock/Unlock media removal */ 55 | #define CTRL_EJECT 7 /* Eject media */ 56 | #define CTRL_FORMAT 8 /* Create physical format on the media */ 57 | 58 | /* MMC/SDC specific ioctl command */ 59 | #define MMC_GET_TYPE 10 /* Get card type */ 60 | #define MMC_GET_CSD 11 /* Get CSD */ 61 | #define MMC_GET_CID 12 /* Get CID */ 62 | #define MMC_GET_OCR 13 /* Get OCR */ 63 | #define MMC_GET_SDSTAT 14 /* Get SD status */ 64 | #define ISDIO_READ 55 /* Read data form SD iSDIO register */ 65 | #define ISDIO_WRITE 56 /* Write data to SD iSDIO register */ 66 | #define ISDIO_MRITE 57 /* Masked write data to SD iSDIO register */ 67 | 68 | /* ATA/CF specific ioctl command */ 69 | #define ATA_GET_REV 20 /* Get F/W revision */ 70 | #define ATA_GET_MODEL 21 /* Get model name */ 71 | #define ATA_GET_SN 22 /* Get serial number */ 72 | 73 | #ifdef __cplusplus 74 | } 75 | #endif 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /drivers/usbfs/storage.c: -------------------------------------------------------------------------------- 1 | /* 2 | * usbfs/storage.cpp - part of the PicoW C/C++ Boilerplate Project 3 | * 4 | * These functions provide a common storage backend, to be used both by TinyUSB 5 | * and FatFS. The storage is simply a small section of flash at the end of memory. 6 | * 7 | * We allocated 128 sectors (~512kb) of storage, which the minimum that FatFS 8 | * will allow us to use. This takes quite a chunk out of the Pico flash, but it 9 | * is what it is. 10 | * 11 | * Copyright (C) 2023 Pete Favelle 12 | * This file is released under the BSD 3-Clause License; see LICENSE for details. 13 | */ 14 | 15 | /* System headers. */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include "hardware/flash.h" 22 | #include "hardware/sync.h" 23 | 24 | 25 | /* Local headers. */ 26 | 27 | #include "usbfs.h" 28 | 29 | 30 | /* Module variables. */ 31 | 32 | static const uint32_t m_storage_size = FLASH_SECTOR_SIZE * 128; 33 | static const uint32_t m_storage_offset = PICO_FLASH_SIZE_BYTES - m_storage_size; 34 | 35 | 36 | /* Functions.*/ 37 | 38 | /* 39 | * get_size - provides size information about storage. 40 | */ 41 | 42 | void storage_get_size( uint16_t *p_block_size, uint32_t *p_num_blocks ) 43 | { 44 | /* Very simple look up. */ 45 | *p_block_size = FLASH_SECTOR_SIZE; 46 | *p_num_blocks = m_storage_size / FLASH_SECTOR_SIZE; 47 | 48 | /* All done. */ 49 | return; 50 | } 51 | 52 | 53 | /* 54 | * read - fetches data from flash. 55 | */ 56 | 57 | int32_t storage_read( uint32_t p_sector, uint32_t p_offset, 58 | void *p_buffer, uint32_t p_size_bytes ) 59 | { 60 | /* Very simple copy out of flash then! */ 61 | memcpy( 62 | p_buffer, 63 | (uint8_t *)XIP_NOCACHE_NOALLOC_BASE + m_storage_offset + p_sector * FLASH_SECTOR_SIZE + p_offset, 64 | p_size_bytes 65 | ); 66 | 67 | /* And return how much we read. */ 68 | return p_size_bytes; 69 | } 70 | 71 | 72 | /* 73 | * write - stores data into flash, with appropriate guards. 74 | */ 75 | 76 | int32_t storage_write( uint32_t p_sector, uint32_t p_offset, 77 | const uint8_t *p_buffer, uint32_t p_size_bytes ) 78 | { 79 | uint32_t l_status; 80 | 81 | /* Don't want to be interrupted. */ 82 | l_status = save_and_disable_interrupts(); 83 | 84 | /* Erasing with an offset of 0? Seems odd, but... */ 85 | if ( p_offset == 0 ) 86 | { 87 | flash_range_erase( m_storage_offset + p_sector * FLASH_SECTOR_SIZE, FLASH_SECTOR_SIZE ); 88 | } 89 | 90 | /* And just write the data now. */ 91 | flash_range_program( 92 | m_storage_offset + p_sector * FLASH_SECTOR_SIZE + p_offset, 93 | p_buffer, p_size_bytes 94 | ); 95 | 96 | /* Lastly, restore our interrupts. */ 97 | restore_interrupts( l_status ); 98 | 99 | /* Before returning the amount of data written. */ 100 | return p_size_bytes; 101 | } 102 | 103 | 104 | /* End of file usbfs/storage.cpp */ 105 | -------------------------------------------------------------------------------- /drivers/usbfs/tusb_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 Ha Thach (tinyusb.org) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #ifndef _TUSB_CONFIG_H_ 27 | #define _TUSB_CONFIG_H_ 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | //-------------------------------------------------------------------- 34 | // COMMON CONFIGURATION 35 | //-------------------------------------------------------------------- 36 | 37 | // defined by board.mk 38 | #ifndef CFG_TUSB_MCU 39 | #error CFG_TUSB_MCU must be defined 40 | #endif 41 | 42 | // RHPort number used for device can be defined by board.mk, default to port 0 43 | #ifndef BOARD_DEVICE_RHPORT_NUM 44 | #define BOARD_DEVICE_RHPORT_NUM 0 45 | #endif 46 | 47 | // RHPort max operational speed can defined by board.mk 48 | // Default to Highspeed for MCU with internal HighSpeed PHY (can be port specific), otherwise FullSpeed 49 | #ifndef BOARD_DEVICE_RHPORT_SPEED 50 | #if (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || \ 51 | CFG_TUSB_MCU == OPT_MCU_NUC505 || CFG_TUSB_MCU == OPT_MCU_CXD56) 52 | #define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_HIGH_SPEED 53 | #else 54 | #define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_FULL_SPEED 55 | #endif 56 | #endif 57 | 58 | // Device mode with rhport and speed defined by board.mk 59 | #if BOARD_DEVICE_RHPORT_NUM == 0 60 | #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED) 61 | #elif BOARD_DEVICE_RHPORT_NUM == 1 62 | #define CFG_TUSB_RHPORT1_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED) 63 | #else 64 | #error "Incorrect RHPort configuration" 65 | #endif 66 | 67 | // This example doesn't use an RTOS 68 | #ifndef CFG_TUSB_OS 69 | #define CFG_TUSB_OS OPT_OS_NONE 70 | #endif 71 | 72 | // CFG_TUSB_DEBUG is defined by compiler in DEBUG build 73 | // #define CFG_TUSB_DEBUG 0 74 | 75 | /* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. 76 | * Tinyusb use follows macros to declare transferring memory so that they can be put 77 | * into those specific section. 78 | * e.g 79 | * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) 80 | * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) 81 | */ 82 | #ifndef CFG_TUSB_MEM_SECTION 83 | #define CFG_TUSB_MEM_SECTION 84 | #endif 85 | 86 | #ifndef CFG_TUSB_MEM_ALIGN 87 | #define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) 88 | #endif 89 | 90 | //-------------------------------------------------------------------- 91 | // DEVICE CONFIGURATION 92 | //-------------------------------------------------------------------- 93 | 94 | #ifndef CFG_TUD_ENDPOINT0_SIZE 95 | #define CFG_TUD_ENDPOINT0_SIZE 64 96 | #endif 97 | 98 | //------------- CLASS -------------// 99 | #define CFG_TUD_CDC 1 100 | #define CFG_TUD_MSC 1 101 | #define CFG_TUD_HID 0 102 | #define CFG_TUD_MIDI 0 103 | #define CFG_TUD_VENDOR 0 104 | 105 | // CDC FIFO size of TX and RX 106 | #define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) 107 | #define CFG_TUD_CDC_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) 108 | 109 | // CDC Endpoint transfer buffer size, more is faster 110 | #define CFG_TUD_CDC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) 111 | 112 | // MSC Buffer size of Device Mass storage 113 | #define CFG_TUD_MSC_EP_BUFSIZE 512 114 | 115 | #ifdef __cplusplus 116 | } 117 | #endif 118 | 119 | #endif /* _TUSB_CONFIG_H_ */ 120 | -------------------------------------------------------------------------------- /drivers/usbfs/usb.c: -------------------------------------------------------------------------------- 1 | /* 2 | * usbfs/usb.cpp - part of the PicoW C/C++ Boilerplate Project 3 | * 4 | * These functions are mostly the callbacks required by TinyUSB; it is largely 5 | * culled from DaftFreak's awesome work on the PicoSystem. 6 | * 7 | * Copyright (C) 2023 Pete Favelle 8 | * This file is released under the BSD 3-Clause License; see LICENSE for details. 9 | */ 10 | 11 | 12 | /* System headers. */ 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | 20 | /* Local headers. */ 21 | 22 | #include "tusb.h" 23 | #include "usbfs.h" 24 | 25 | 26 | /* Module variables. */ 27 | 28 | static bool m_mounted = true; 29 | static bool m_fs_changed = false; 30 | 31 | 32 | /* Functions.*/ 33 | 34 | /* 35 | * usb_set_fs_changed - sets the flag to indicate that a file has been locally 36 | * changed; in turn, this ensures that the host is told 37 | * that a re-scan is required. 38 | */ 39 | 40 | void usb_set_fs_changed( void ) 41 | { 42 | /* Very simple flag set. */ 43 | m_fs_changed = true; 44 | return; 45 | } 46 | 47 | 48 | /* 49 | * tud_mount_cb - Invoked when device is mounted (configured) 50 | */ 51 | 52 | void tud_mount_cb( void ) 53 | { 54 | /* All we really do is remember that we're mounted. */ 55 | m_mounted = true; 56 | return; 57 | } 58 | 59 | 60 | /* 61 | * tud_umount_cb - Invoked when device is unmounted 62 | */ 63 | 64 | void tud_umount_cb( void ) 65 | { 66 | /* As before, just remember this. */ 67 | m_mounted = false; 68 | return; 69 | } 70 | 71 | 72 | /* Mass Storage (MSC) callbacks. */ 73 | 74 | /* 75 | * tud_msc_read10_cb - Invoked when received SCSI READ10 command 76 | * 77 | * Fill the buffer (up to bufsize) with address contents and return number of 78 | * read bytes. 79 | * 80 | * If read < bufsize, these bytes are transferred first and callback invoked 81 | * again for remaining data. 82 | * 83 | * If read == 0, it indicates we're not ready yet e.g disk I/O busy. Callback 84 | * will be invoked again with the same parameters later on. 85 | * 86 | * If read < 0, indicates an error e.g invalid address. This request will be 87 | * STALLed and return failed status in command status wrapper phase. 88 | */ 89 | 90 | int32_t tud_msc_read10_cb( uint8_t p_lun, uint32_t p_lba, 91 | uint32_t p_offset, void *p_buffer, 92 | uint32_t p_bufsize ) 93 | { 94 | /* We simply pass on this request to the storage layer. */ 95 | return storage_read( p_lba, p_offset, p_buffer, p_bufsize ); 96 | } 97 | 98 | 99 | /* 100 | * tud_msc_write10_cd - Invoked when received SCSI WRITE10 command 101 | * 102 | * Write data from buffer to address (up to bufsize) and return number of 103 | * written bytes. 104 | * 105 | * If write < bufsize, callback invoked again with remaining data later on. 106 | * 107 | * If write == 0, it indicates we're not ready yet e.g disk I/O busy. 108 | * Callback will be invoked again with the same parameters later on. 109 | * 110 | * If write < 0, it indicates an error e.g invalid address. This request will 111 | * be STALLed and return failed status in command status wrapper phase. 112 | */ 113 | 114 | int32_t tud_msc_write10_cb( uint8_t p_lun, uint32_t p_lba, 115 | uint32_t p_offset, uint8_t *p_buffer, 116 | uint32_t p_bufsize ) 117 | { 118 | /* We simply pass on this request to the storage layer. */ 119 | return storage_write( p_lba, p_offset, p_buffer, p_bufsize ); 120 | } 121 | 122 | 123 | /* 124 | * tud_msc_inquiry_cb - Invoked when received SCSI_CMD_INQUIRY 125 | * 126 | * Return the vendor, product and revision strings in the provider buffers. 127 | */ 128 | 129 | void tud_msc_inquiry_cb( uint8_t p_lun, uint8_t p_vendor_id[8], 130 | uint8_t p_product_id[16], uint8_t p_product_rev[4] ) 131 | { 132 | const char *l_vid = USB_VENDOR_STR; 133 | const char *l_pid = USB_PRODUCT_STR " Storage"; 134 | const char *l_rev = "1.0"; 135 | 136 | memcpy( p_vendor_id , l_vid, strlen(l_vid) ); 137 | memcpy( p_product_id , l_pid, strlen(l_pid) ); 138 | memcpy( p_product_rev, l_rev, strlen(l_rev) ); 139 | } 140 | 141 | 142 | /* 143 | * tud_msc_test_unit_ready_cb - Invoked when received Test Unit Ready command. 144 | * 145 | * If we have made local changes to files, we raise a SENSE_NOT_READY and return 146 | * false to indicate that the host needs to re-scan. Otherwise, true indicates 147 | * that all is well. 148 | */ 149 | 150 | bool tud_msc_test_unit_ready_cb( uint8_t p_lun ) 151 | { 152 | /* If files have changed locally, or if we're not mounted, we're not ready. */ 153 | if( m_fs_changed || !m_mounted ) 154 | { 155 | tud_msc_set_sense( p_lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00 ); 156 | m_fs_changed = false; 157 | return false; 158 | } 159 | 160 | /* Otherwise, we are. */ 161 | return true; 162 | } 163 | 164 | 165 | /* 166 | * tud_msc_capacity_cb - Invoked when received SCSI_CMD_READ_CAPACITY_10 and 167 | * SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size. 168 | * 169 | * Simply return information about our block size and counts. 170 | */ 171 | 172 | void tud_msc_capacity_cb( uint8_t p_lun, 173 | uint32_t *p_block_count, uint16_t *p_block_size ) 174 | { 175 | /* This is a question for the storage layer. */ 176 | storage_get_size( p_block_size, p_block_count ); 177 | return; 178 | } 179 | 180 | 181 | /* 182 | * tud_msc_scsi_cb - Invoked when receivind a SCSI command not handled by its 183 | * own callback. 184 | * 185 | * Returns the actual bytes processed, can be zero for no-data command. 186 | * A negative return indicates error e.g unsupported command, tinyusb will STALL 187 | * the corresponding endpoint and return failed status in command status wrapper phase. 188 | */ 189 | 190 | int32_t tud_msc_scsi_cb( uint8_t p_lun, uint8_t const p_scsi_cmd[16], 191 | void *p_buffer, uint16_t p_bufsize ) 192 | { 193 | int32_t l_retval = 0; 194 | 195 | /* Decide what we're doing based on the SCSI command. */ 196 | switch ( p_scsi_cmd[0] ) 197 | { 198 | /* This is fine. */ 199 | case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: 200 | l_retval = 0; 201 | break; 202 | 203 | /* By default, send an ILLEGAL_REQUEST back, and fail. */ 204 | default: 205 | tud_msc_set_sense( p_lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00 ); 206 | l_retval = -1; 207 | break; 208 | } 209 | 210 | /* Return the appopriate code. */ 211 | return l_retval; 212 | } 213 | 214 | 215 | /* 216 | * tud_msc_start_stop_cb - Invoked when received Start Stop Unit command 217 | * 218 | * start = 0 : stopped power mode, if load_eject = 1 : unload disk storage 219 | * start = 1 : active mode, if load_eject = 1 : load disk storage 220 | */ 221 | 222 | bool tud_msc_start_stop_cb( uint8_t p_lun, uint8_t p_power_condition, 223 | bool p_start, bool p_load_eject ) 224 | { 225 | /* If the load_eject flag isn't set, we don't need to do anything. */ 226 | if( p_load_eject ) 227 | { 228 | /* If not starting, we're unloading our drive - flag it as unmounted. */ 229 | if ( !p_start ) 230 | { 231 | m_mounted = false; 232 | } 233 | } 234 | 235 | /* All is well. */ 236 | return true; 237 | } 238 | 239 | 240 | /* 241 | * tud_msc_is_writable_cb - Invoked to check if device is writable 242 | */ 243 | 244 | bool tud_msc_is_writable_cb( uint8_t p_lun ) 245 | { 246 | /* We're always writable. */ 247 | return true; 248 | } 249 | 250 | 251 | /* Communications Device (CDC) callbacks. */ 252 | 253 | /* We don't implement any of these, leaving them to the Pico stdio handling. */ 254 | 255 | 256 | /* End of file usbfs/usb.cpp */ 257 | -------------------------------------------------------------------------------- /drivers/usbfs/usb_descriptors.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 Ha Thach (tinyusb.org) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #include "tusb.h" 27 | #include "usbfs.h" 28 | #include "pico/unique_id.h" 29 | 30 | /* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. 31 | * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. 32 | * 33 | * Auto ProductID layout's Bitmap: 34 | * [MSB] HID | MSC | CDC [LSB] 35 | */ 36 | #ifndef USB_PRODUCT_ID 37 | #define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) ) 38 | #define USB_PRODUCT_ID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \ 39 | _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) ) 40 | #endif 41 | 42 | //--------------------------------------------------------------------+ 43 | // Device Descriptors 44 | //--------------------------------------------------------------------+ 45 | tusb_desc_device_t const desc_device = 46 | { 47 | .bLength = sizeof(tusb_desc_device_t), 48 | .bDescriptorType = TUSB_DESC_DEVICE, 49 | .bcdUSB = 0x0200, 50 | 51 | // Use Interface Association Descriptor (IAD) for CDC 52 | // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1) 53 | .bDeviceClass = TUSB_CLASS_MISC, 54 | .bDeviceSubClass = MISC_SUBCLASS_COMMON, 55 | .bDeviceProtocol = MISC_PROTOCOL_IAD, 56 | 57 | .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, 58 | 59 | .idVendor = USB_VENDOR_ID, 60 | .idProduct = USB_PRODUCT_ID, 61 | .bcdDevice = 0x0100, 62 | 63 | .iManufacturer = 0x01, 64 | .iProduct = 0x02, 65 | .iSerialNumber = 0x03, 66 | 67 | .bNumConfigurations = 0x01 68 | }; 69 | 70 | // Invoked when received GET DEVICE DESCRIPTOR 71 | // Application return pointer to descriptor 72 | uint8_t const * tud_descriptor_device_cb(void) 73 | { 74 | return (uint8_t const *) &desc_device; 75 | } 76 | 77 | //--------------------------------------------------------------------+ 78 | // Configuration Descriptor 79 | //--------------------------------------------------------------------+ 80 | 81 | enum 82 | { 83 | ITF_NUM_CDC = 0, 84 | ITF_NUM_CDC_DATA, 85 | ITF_NUM_MSC, 86 | ITF_NUM_TOTAL 87 | }; 88 | 89 | #define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_MSC_DESC_LEN) 90 | 91 | #define EPNUM_CDC_NOTIF 0x81 92 | #define EPNUM_CDC_OUT 0x02 93 | #define EPNUM_CDC_IN 0x82 94 | 95 | #define EPNUM_MSC_OUT 0x03 96 | #define EPNUM_MSC_IN 0x83 97 | 98 | uint8_t const desc_fs_configuration[] = 99 | { 100 | // Config number, interface count, string index, total length, attribute, power in mA 101 | TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), 102 | 103 | // Interface number, string index, EP notification address and size, EP data address (out, in) and size. 104 | TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64), 105 | 106 | // Interface number, string index, EP Out & EP In address, EP size 107 | TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, 64), 108 | }; 109 | 110 | 111 | // Invoked when received GET CONFIGURATION DESCRIPTOR 112 | // Application return pointer to descriptor 113 | // Descriptor contents must exist long enough for transfer to complete 114 | uint8_t const * tud_descriptor_configuration_cb(uint8_t index) 115 | { 116 | (void) index; // for multiple configurations 117 | 118 | return desc_fs_configuration; 119 | } 120 | 121 | //--------------------------------------------------------------------+ 122 | // String Descriptors 123 | //--------------------------------------------------------------------+ 124 | 125 | // array of pointer to string descriptors 126 | char const* string_desc_arr [] = 127 | { 128 | (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) 129 | USB_VENDOR_STR, // 1: Manufacturer 130 | USB_PRODUCT_STR, // 2: Product 131 | NULL, // 3: Serials, should use chip ID 132 | USB_PRODUCT_STR" CDC", // 4: CDC Interface 133 | USB_PRODUCT_STR" MSC", // 5: MSC Interface 134 | }; 135 | 136 | static uint16_t _desc_str[32]; 137 | 138 | // Invoked when received GET STRING DESCRIPTOR request 139 | // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete 140 | uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) 141 | { 142 | (void) langid; 143 | 144 | uint8_t chr_count; 145 | 146 | if ( index == 0) 147 | { 148 | memcpy(&_desc_str[1], string_desc_arr[0], 2); 149 | chr_count = 1; 150 | } 151 | else 152 | { 153 | // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. 154 | // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors 155 | 156 | if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL; 157 | 158 | const char* str = string_desc_arr[index]; 159 | 160 | // get qnique id/serial 161 | char serial[PICO_UNIQUE_BOARD_ID_SIZE_BYTES * 2 + 1]; 162 | if(!str) { 163 | pico_get_unique_board_id_string(serial, sizeof(serial)); 164 | str = serial; 165 | } 166 | 167 | // Cap at max char 168 | chr_count = strlen(str); 169 | if ( chr_count > 31 ) chr_count = 31; 170 | 171 | // Convert ASCII string into UTF-16 172 | for(uint8_t i=0; i 9 | * This file is released under the BSD 3-Clause License; see LICENSE for details. 10 | */ 11 | 12 | #pragma once 13 | 14 | #include "ff.h" 15 | 16 | 17 | /* Constants. */ 18 | 19 | #define USB_VENDOR_ID 0xCafe 20 | #define USB_VENDOR_STR "USBFS" 21 | #define USB_PRODUCT_STR "PicoW" 22 | 23 | #define UFS_LABEL "PicoW" 24 | 25 | 26 | /* Structures */ 27 | 28 | typedef struct 29 | { 30 | FIL fatfs_fptr; 31 | bool modified; 32 | } usbfs_file_t; 33 | 34 | 35 | /* Function prototypes. */ 36 | 37 | /* Internal functions. */ 38 | 39 | void storage_get_size( uint16_t *, uint32_t * ); 40 | int32_t storage_read( uint32_t, uint32_t, void *, uint32_t ); 41 | int32_t storage_write( uint32_t, uint32_t, const uint8_t *, uint32_t ); 42 | 43 | void usb_set_fs_changed( void ); 44 | 45 | /* Public functions. */ 46 | 47 | #ifdef __cplusplus 48 | extern "C" { 49 | #endif 50 | 51 | void usbfs_init( void ); 52 | void usbfs_update( void ); 53 | void usbfs_sleep_ms( uint32_t ); 54 | 55 | usbfs_file_t *usbfs_open( const char *, const char * ); 56 | bool usbfs_close( usbfs_file_t * ); 57 | size_t usbfs_read( void *, size_t, usbfs_file_t * ); 58 | size_t usbfs_write( const void *, size_t, usbfs_file_t * ); 59 | char *usbfs_gets( char *, size_t, usbfs_file_t * ); 60 | size_t usbfs_puts( const char *, usbfs_file_t * ); 61 | uint32_t usbfs_timestamp( const char * ); 62 | 63 | #ifdef __cplusplus 64 | } 65 | #endif 66 | 67 | 68 | /* End of file usbfs/usbfs.h */ 69 | -------------------------------------------------------------------------------- /drivers/vga-nextgen/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(vga-nextgen INTERFACE) 2 | 3 | target_sources(vga-nextgen INTERFACE 4 | ${CMAKE_CURRENT_LIST_DIR}/vga.c 5 | ) 6 | 7 | target_link_libraries(vga-nextgen INTERFACE hardware_pio hardware_dma) 8 | 9 | target_include_directories(vga-nextgen INTERFACE 10 | ${CMAKE_CURRENT_LIST_DIR} 11 | ) 12 | -------------------------------------------------------------------------------- /drivers/vga-nextgen/vga.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "stdbool.h" 3 | 4 | #define PIO_VGA (pio0) 5 | #ifndef VGA_BASE_PIN 6 | #define VGA_BASE_PIN (6) 7 | #endif 8 | #define VGA_DMA_IRQ (DMA_IRQ_0) 9 | 10 | #define TEXTMODE_COLS 80 11 | #define TEXTMODE_ROWS 30 12 | 13 | #define RGB888(r, g, b) ((r<<16) | (g << 8 ) | b ) 14 | -------------------------------------------------------------------------------- /drivers/ws2812/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(ws2812 INTERFACE) 2 | 3 | target_sources(ws2812 INTERFACE 4 | ${CMAKE_CURRENT_LIST_DIR}/ws2812.c 5 | ${CMAKE_CURRENT_LIST_DIR}/ws2812.h 6 | ) 7 | 8 | target_link_libraries(ws2812 INTERFACE hardware_pio hardware_clocks) 9 | 10 | target_include_directories(ws2812 INTERFACE 11 | ${CMAKE_CURRENT_LIST_DIR} 12 | ) 13 | 14 | pico_generate_pio_header(ws2812 15 | ${CMAKE_CURRENT_LIST_DIR}/ws2812.pio 16 | ) 17 | -------------------------------------------------------------------------------- /drivers/ws2812/ws2812.c: -------------------------------------------------------------------------------- 1 | #include "ws2812.h" 2 | #include "ws2812.pio.h" 3 | 4 | 5 | void ws2812_set_rgb(uint8_t red, uint8_t green, uint8_t blue) { 6 | pio_sm_restart(WS2812_PIO, WS2812_SM); 7 | 8 | // write the rgb 9 | uint32_t mask = (green << 16) | (red << 8) | (blue << 0); 10 | pio_sm_put_blocking(WS2812_PIO, WS2812_SM, mask << 8u); 11 | } 12 | 13 | void ws2812_init() { 14 | ws2812_program_init(WS2812_PIO, WS2812_SM, WS2812_PIN, true); 15 | } 16 | 17 | void ws2812_reset() { 18 | // turn it off 19 | ws2812_set_rgb(0, 0, 0); 20 | pio_sm_set_enabled(WS2812_PIO, WS2812_SM, false); 21 | pio_sm_restart(WS2812_PIO, WS2812_SM); 22 | } 23 | -------------------------------------------------------------------------------- /drivers/ws2812/ws2812.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "hardware/pio.h" 3 | 4 | #define WS2812_SM 1 5 | #define WS2812_PIO ((pio_hw_t *)PIO1_BASE) 6 | #define WS2812_PIN 23 7 | 8 | void ws2812_set_rgb(uint8_t red, uint8_t green, uint8_t blue); 9 | void ws2812_init(); 10 | void ws2812_reset(); 11 | -------------------------------------------------------------------------------- /drivers/ws2812/ws2812.pio: -------------------------------------------------------------------------------- 1 | ; 2 | ; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | ; 4 | ; SPDX-License-Identifier: BSD-3-Clause 5 | ; 6 | 7 | .program ws2812 8 | .side_set 1 9 | 10 | .define public T1 2 11 | .define public T2 5 12 | .define public T3 3 13 | 14 | .lang_opt python sideset_init = pico.PIO.OUT_HIGH 15 | .lang_opt python out_init = pico.PIO.OUT_HIGH 16 | .lang_opt python out_shiftdir = 1 17 | 18 | .wrap_target 19 | bitloop: 20 | out x, 1 side 0 [T3 - 1] ; Side-set still takes place when instruction stalls 21 | jmp !x do_zero side 1 [T1 - 1] ; Branch on the bit we shifted out. Positive pulse 22 | do_one: 23 | jmp bitloop side 1 [T2 - 1] ; Continue driving high, for a long pulse 24 | do_zero: 25 | nop side 0 [T2 - 1] ; Or drive low, for a short pulse 26 | .wrap 27 | 28 | % c-sdk { 29 | #include "hardware/clocks.h" 30 | 31 | static inline void ws2812_program_init(PIO pio, uint sm, uint pin, bool rgbw) { 32 | pio_gpio_init(pio, pin); 33 | pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); 34 | 35 | uint offset = pio_add_program(WS2812_PIO, &ws2812_program); 36 | pio_sm_config c = ws2812_program_get_default_config(offset); 37 | sm_config_set_sideset_pins(&c, pin); 38 | sm_config_set_out_shift(&c, false, true, rgbw ? 32 : 24); 39 | sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); 40 | 41 | const int cycles_per_bit = ws2812_T1 + ws2812_T2 + ws2812_T3; 42 | float div = clock_get_hz(clk_sys) / (800000 * cycles_per_bit); 43 | sm_config_set_clkdiv(&c, div); 44 | 45 | pio_sm_init(pio, sm, offset, &c); 46 | pio_sm_set_enabled(pio, sm, true); 47 | } 48 | %} 49 | -------------------------------------------------------------------------------- /memmap_genesis.ld: -------------------------------------------------------------------------------- 1 | /* Based on GCC ARM embedded samples. 2 | Defines the following symbols for use by code: 3 | __exidx_start 4 | __exidx_end 5 | __etext 6 | __data_start__ 7 | __preinit_array_start 8 | __preinit_array_end 9 | __init_array_start 10 | __init_array_end 11 | __fini_array_start 12 | __fini_array_end 13 | __data_end__ 14 | __bss_start__ 15 | __bss_end__ 16 | __end__ 17 | end 18 | __HeapLimit 19 | __StackLimit 20 | __StackTop 21 | __stack (== StackTop) 22 | */ 23 | 24 | MEMORY 25 | { 26 | FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 16384k 27 | RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k 28 | SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k 29 | SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k 30 | } 31 | 32 | ENTRY(_entry_point) 33 | 34 | SECTIONS 35 | { 36 | /* Second stage bootloader is prepended to the image. It must be 256 bytes big 37 | and checksummed. It is usually built by the boot_stage2 target 38 | in the Raspberry Pi Pico SDK 39 | */ 40 | 41 | .flash_begin : { 42 | __flash_binary_start = .; 43 | } > FLASH 44 | 45 | .boot2 : { 46 | __boot2_start__ = .; 47 | KEEP (*(.boot2)) 48 | __boot2_end__ = .; 49 | } > FLASH 50 | 51 | ASSERT(__boot2_end__ - __boot2_start__ == 256, 52 | "ERROR: Pico second stage bootloader must be 256 bytes in size") 53 | 54 | /* The second stage will always enter the image at the start of .text. 55 | The debugger will use the ELF entry point, which is the _entry_point 56 | symbol if present, otherwise defaults to start of .text. 57 | This can be used to transfer control back to the bootrom on debugger 58 | launches only, to perform proper flash setup. 59 | */ 60 | 61 | .text : { 62 | __logical_binary_start = .; 63 | KEEP (*(.vectors)) 64 | KEEP (*(.binary_info_header)) 65 | __binary_info_header_end = .; 66 | KEEP (*(.reset)) 67 | /* TODO revisit this now memset/memcpy/float in ROM */ 68 | /* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from 69 | * FLASH ... we will include any thing excluded here in .data below by default */ 70 | *(.init) 71 | *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*) 72 | *(.fini) 73 | /* Pull all c'tors into .text */ 74 | *crtbegin.o(.ctors) 75 | *crtbegin?.o(.ctors) 76 | *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) 77 | *(SORT(.ctors.*)) 78 | *(.ctors) 79 | /* Followed by destructors */ 80 | *crtbegin.o(.dtors) 81 | *crtbegin?.o(.dtors) 82 | *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) 83 | *(SORT(.dtors.*)) 84 | *(.dtors) 85 | 86 | *(.eh_frame*) 87 | . = ALIGN(4); 88 | } > FLASH 89 | 90 | .rodata : { 91 | *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*) 92 | . = ALIGN(4); 93 | *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*))) 94 | . = ALIGN(4); 95 | } > FLASH 96 | 97 | .ARM.extab : 98 | { 99 | *(.ARM.extab* .gnu.linkonce.armextab.*) 100 | } > FLASH 101 | 102 | __exidx_start = .; 103 | .ARM.exidx : 104 | { 105 | *(.ARM.exidx* .gnu.linkonce.armexidx.*) 106 | } > FLASH 107 | __exidx_end = .; 108 | 109 | /* Machine inspectable binary information */ 110 | . = ALIGN(4); 111 | __binary_info_start = .; 112 | .binary_info : 113 | { 114 | KEEP(*(.binary_info.keep.*)) 115 | *(.binary_info.*) 116 | } > FLASH 117 | __binary_info_end = .; 118 | . = ALIGN(4); 119 | 120 | .ram_vector_table (NOLOAD): { 121 | *(.ram_vector_table) 122 | } > RAM 123 | 124 | .data : { 125 | __data_start__ = .; 126 | *(vtable) 127 | 128 | *(.time_critical*) 129 | 130 | /* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */ 131 | *(.text*) 132 | . = ALIGN(4); 133 | *(.rodata*) 134 | . = ALIGN(4); 135 | 136 | *(.data*) 137 | 138 | . = ALIGN(4); 139 | *(.after_data.*) 140 | . = ALIGN(4); 141 | /* preinit data */ 142 | PROVIDE_HIDDEN (__mutex_array_start = .); 143 | KEEP(*(SORT(.mutex_array.*))) 144 | KEEP(*(.mutex_array)) 145 | PROVIDE_HIDDEN (__mutex_array_end = .); 146 | 147 | . = ALIGN(4); 148 | /* preinit data */ 149 | PROVIDE_HIDDEN (__preinit_array_start = .); 150 | KEEP(*(SORT(.preinit_array.*))) 151 | KEEP(*(.preinit_array)) 152 | PROVIDE_HIDDEN (__preinit_array_end = .); 153 | 154 | . = ALIGN(4); 155 | /* init data */ 156 | PROVIDE_HIDDEN (__init_array_start = .); 157 | KEEP(*(SORT(.init_array.*))) 158 | KEEP(*(.init_array)) 159 | PROVIDE_HIDDEN (__init_array_end = .); 160 | 161 | . = ALIGN(4); 162 | /* finit data */ 163 | PROVIDE_HIDDEN (__fini_array_start = .); 164 | *(SORT(.fini_array.*)) 165 | *(.fini_array) 166 | PROVIDE_HIDDEN (__fini_array_end = .); 167 | 168 | *(.jcr) 169 | . = ALIGN(4); 170 | /* All data end */ 171 | __data_end__ = .; 172 | } > RAM AT> FLASH 173 | /* __etext is (for backwards compatibility) the name of the .data init source pointer (...) */ 174 | __etext = LOADADDR(.data); 175 | 176 | .uninitialized_data (NOLOAD): { 177 | . = ALIGN(4); 178 | *(.uninitialized_data*) 179 | } > RAM 180 | 181 | /* Start and end symbols must be word-aligned */ 182 | .scratch_x : { 183 | __scratch_x_start__ = .; 184 | *(.scratch_x.*) 185 | . = ALIGN(4); 186 | __scratch_x_end__ = .; 187 | } > SCRATCH_X AT > FLASH 188 | __scratch_x_source__ = LOADADDR(.scratch_x); 189 | 190 | .scratch_y : { 191 | __scratch_y_start__ = .; 192 | *(.scratch_y.*) 193 | . = ALIGN(4); 194 | __scratch_y_end__ = .; 195 | } > SCRATCH_Y AT > FLASH 196 | __scratch_y_source__ = LOADADDR(.scratch_y); 197 | 198 | .bss : { 199 | . = ALIGN(4); 200 | __bss_start__ = .; 201 | *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*))) 202 | *(COMMON) 203 | . = ALIGN(4); 204 | __bss_end__ = .; 205 | } > RAM 206 | 207 | .heap (NOLOAD): 208 | { 209 | __end__ = .; 210 | end = __end__; 211 | KEEP(*(.heap*)) 212 | __HeapLimit = .; 213 | } > RAM 214 | 215 | /* .stack*_dummy section doesn't contains any symbols. It is only 216 | * used for linker to calculate size of stack sections, and assign 217 | * values to stack symbols later 218 | * 219 | * stack1 section may be empty/missing if platform_launch_core1 is not used */ 220 | 221 | /* by default we put core 0 stack at the end of scratch Y, so that if core 1 222 | * stack is not used then all of SCRATCH_X is free. 223 | */ 224 | .stack1_dummy (NOLOAD): 225 | { 226 | *(.stack1*) 227 | } > SCRATCH_X 228 | .stack_dummy (NOLOAD): 229 | { 230 | KEEP(*(.stack*)) 231 | } > SCRATCH_Y 232 | 233 | .flash_end : { 234 | PROVIDE(__flash_binary_end = .); 235 | } > FLASH 236 | 237 | /* stack limit is poorly named, but historically is maximum heap ptr */ 238 | __StackLimit = ORIGIN(RAM) + LENGTH(RAM); 239 | __StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X); 240 | __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y); 241 | __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy); 242 | __StackBottom = __StackTop - SIZEOF(.stack_dummy); 243 | PROVIDE(__stack = __StackTop); 244 | 245 | /* Check if data + heap + stack exceeds RAM limit */ 246 | ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed") 247 | 248 | ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary") 249 | /* todo assert on extra code */ 250 | } 251 | 252 | -------------------------------------------------------------------------------- /pico_sdk_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_sdk_import.cmake 2 | 3 | # This can be dropped into an external project to help locate this SDK 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) 7 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 8 | message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) 12 | set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) 13 | message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) 17 | set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") 22 | set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") 23 | set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") 24 | 25 | if (NOT PICO_SDK_PATH) 26 | if (PICO_SDK_FETCH_FROM_GIT) 27 | include(FetchContent) 28 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 29 | if (PICO_SDK_FETCH_FROM_GIT_PATH) 30 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 31 | endif () 32 | FetchContent_Declare( 33 | pico_sdk 34 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 35 | GIT_TAG master 36 | ) 37 | if (NOT pico_sdk) 38 | message("Downloading Raspberry Pi Pico SDK") 39 | FetchContent_Populate(pico_sdk) 40 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) 41 | endif () 42 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 43 | else () 44 | message(FATAL_ERROR 45 | "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." 46 | ) 47 | endif () 48 | endif () 49 | 50 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 51 | if (NOT EXISTS ${PICO_SDK_PATH}) 52 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") 53 | endif () 54 | 55 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) 56 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) 57 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") 58 | endif () 59 | 60 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) 61 | 62 | include(${PICO_SDK_INIT_CMAKE_FILE}) 63 | -------------------------------------------------------------------------------- /src/gwenesis/bus/gwenesis_bus.h: -------------------------------------------------------------------------------- 1 | /* 2 | Gwenesis : Genesis & megadrive Emulator. 3 | 4 | This program is free software: you can redistribute it and/or modify it under 5 | the terms of the GNU General Public License as published by the Free Software 6 | Foundation, either version 3 of the License, or (at your option) any later 7 | version. 8 | This program is distributed in the hope that it will be useful, but WITHOUT 9 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 10 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 11 | You should have received a copy of the GNU General Public License along with 12 | this program. If not, see . 13 | 14 | __author__ = "bzhxx" 15 | __contact__ = "https://github.com/bzhxx" 16 | __license__ = "GPLv3" 17 | 18 | */ 19 | #ifndef _gwenesis_bus_H_ 20 | #define _gwenesis_bus_H_ 21 | 22 | #pragma once 23 | 24 | #include 25 | #include 26 | 27 | #define MAX_ROM_SIZE 0x800000 28 | #define MAX_RAM_SIZE 0x10000 29 | #define MAX_Z80_RAM_SIZE 8192 30 | 31 | // NTSC PAL timings 32 | #define MCLOCK_PAL 53203424 33 | #define MCLOCK_NTSC 53693175 34 | 35 | #define MCYCLES_PER_FRAME_NTSC 896040 36 | #define MCYCLES_PER_FRAME_PAL 1067040 37 | #define LINES_PER_FRAME_NTSC 262 38 | #define LINES_PER_FRAME_PAL 313 39 | 40 | #define GWENESIS_REFRESH_RATE_NTSC 60 41 | #define GWENESIS_AUDIO_FREQ_NTSC 53267 42 | 43 | #define GWENESIS_REFRESH_RATE_PAL 50 44 | #define GWENESIS_AUDIO_FREQ_PAL 52781 45 | 46 | #define Z80_FREQ_DIVISOR 14 // Frequency divisor to Z80 clock 47 | #define VDP_CYCLES_PER_LINE 3420// VDP Cycles per Line 48 | #define GWENESIS_SCREEN_WIDTH 320 49 | #define GWENESIS_SCREEN_HEIGHT 240 50 | 51 | extern uint8_t GWENESIS_AUDIO_SAMPLING_DIVISOR; // Audio quality (i.e. sampling rate) 1: best ... 10: poor 52 | 53 | #define AUDIO_FREQ_DIVISOR 1009 //1009 54 | #define GWENESIS_AUDIO_BUFFER_LENGTH_NTSC 888 55 | #define GWENESIS_AUDIO_BUFFER_LENGTH_PAL 1056 56 | 57 | /* Audio buffer length */ 58 | 59 | enum mapped_address 60 | { 61 | NONE = 0, 62 | ROM_ADDR, 63 | ROM_ADDR_MIRROR, 64 | Z80_RAM_ADDR, 65 | Z80_RAM_ADDR1K, 66 | Z80_YM2612_ADDR, 67 | Z80_BANK_ADDR, 68 | Z80_VDP_ADDR, 69 | Z80_SN76489_ADDR, 70 | IO_CTRL, 71 | Z80_CTRL, 72 | TMSS_CTRL, 73 | VDP_ADDR, 74 | RAM_ADDR 75 | }; 76 | 77 | enum gwenesis_bus_pad_button 78 | { 79 | PAD_UP, 80 | PAD_DOWN, 81 | PAD_LEFT, 82 | PAD_RIGHT, 83 | PAD_B, 84 | PAD_C, 85 | PAD_A, 86 | PAD_S, 87 | }; 88 | 89 | void load_cartridge(uintptr_t rom); 90 | 91 | void power_on(); 92 | void reset_emulation(); 93 | void set_region(); 94 | 95 | void gwenesis_bus_save_state(); 96 | void gwenesis_bus_load_state(); 97 | 98 | #endif 99 | -------------------------------------------------------------------------------- /src/gwenesis/cpus/M68K/m68kconf.h: -------------------------------------------------------------------------------- 1 | #ifndef M68KCONF__HEADER 2 | #define M68KCONF__HEADER 3 | 4 | /* ======================================================================== */ 5 | /* ======================== MAIN 68K CONFIGURATION ======================== */ 6 | /* ======================================================================== */ 7 | 8 | /* Configuration switches. 9 | * Use OPT_SPECIFY_HANDLER for configuration options that allow callbacks. 10 | * OPT_SPECIFY_HANDLER causes the core to link directly to the function 11 | * or macro you specify, rather than using callback functions whose pointer 12 | * must be passed in using m68k_set_xxx_callback(). 13 | */ 14 | #define OPT_OFF 0 15 | #define OPT_ON 1 16 | #define OPT_SPECIFY_HANDLER 2 17 | 18 | /* If ON, the CPU will call m68k_write_32_pd() when it executes move.l with a 19 | * predecrement destination EA mode instead of m68k_write_32(). 20 | * To simulate real 68k behavior, m68k_write_32_pd() must first write the high 21 | * word to [address+2], and then write the low word to [address]. 22 | */ 23 | #define M68K_SIMULATE_PD_WRITES OPT_OFF 24 | 25 | /* If ON, CPU will call the interrupt acknowledge callback when it services an 26 | * interrupt. 27 | * If off, all interrupts will be autovectored and all interrupt requests will 28 | * auto-clear when the interrupt is serviced. 29 | */ 30 | #define M68K_EMULATE_INT_ACK OPT_ON //OPT_SPECIFY_HANDLER 31 | #define M68K_INT_ACK_CALLBACK(A) your_int_ack_handler_function(A) 32 | 33 | /* If ON, CPU will call the output reset callback when it encounters a reset 34 | * instruction. 35 | */ 36 | #define M68K_EMULATE_RESET OPT_OFF 37 | #define M68K_RESET_CALLBACK() your_reset_handler_function() 38 | 39 | /* If ON, CPU will call the callback when it encounters a tas 40 | * instruction. 41 | */ 42 | #define M68K_TAS_HAS_CALLBACK OPT_OFF 43 | #define M68K_TAS_CALLBACK() your_tas_handler_function() 44 | 45 | /* If ON, CPU will call the set fc callback on every memory access to 46 | * differentiate between user/supervisor, program/data access like a real 47 | * 68000 would. This should be enabled and the callback should be set if you 48 | * want to properly emulate the m68010 or higher. (moves uses function codes 49 | * to read/write data from different address spaces) 50 | */ 51 | #define M68K_EMULATE_FC OPT_OFF 52 | #define M68K_SET_FC_CALLBACK(A) your_set_fc_handler_function(A) 53 | 54 | /* If ON, the CPU will monitor the trace flags and take trace exceptions 55 | */ 56 | #define M68K_EMULATE_TRACE OPT_OFF 57 | 58 | /* If ON, the CPU will emulate the 4-byte prefetch queue of a real 68000 */ 59 | #define M68K_EMULATE_PREFETCH OPT_OFF 60 | 61 | /* If ON, the CPU will generate address error exceptions if it tries to 62 | * access a word or longword at an odd address. 63 | * NOTE: This is only emulated properly for 68000 mode. 64 | */ 65 | #define M68K_EMULATE_ADDRESS_ERROR OPT_ON 66 | 67 | /* If ON and previous option is also ON, address error exceptions will 68 | also be checked when fetching instructions. Disabling this can help 69 | speeding up emulation while still emulating address error exceptions 70 | on other memory access if needed. 71 | * NOTE: This is only emulated properly for 68000 mode. 72 | */ 73 | #define M68K_CHECK_PC_ADDRESS_ERROR OPT_OFF 74 | 75 | 76 | /* ----------------------------- COMPATIBILITY ---------------------------- */ 77 | 78 | /* The following options set optimizations that violate the current ANSI 79 | * standard, but will be compliant under the forthcoming C9X standard. 80 | */ 81 | 82 | 83 | /* If ON, the enulation core will use 64-bit integers to speed up some 84 | * operations. 85 | */ 86 | #define M68K_USE_64_BIT OPT_OFF 87 | 88 | 89 | /* ======================================================================== */ 90 | /* ============================== END OF FILE ============================= */ 91 | /* ======================================================================== */ 92 | 93 | #endif /* M68KCONF__HEADER */ 94 | -------------------------------------------------------------------------------- /src/gwenesis/cpus/M68K/macros.h: -------------------------------------------------------------------------------- 1 | #ifndef _MACROS_H_ 2 | #define _MACROS_H_ 3 | 4 | #ifdef LSB_FIRST 5 | 6 | #define READ_BYTE(BASE, ADDR) (BASE)[(ADDR)^1] 7 | 8 | #define READ_WORD(BASE, ADDR) (((BASE)[ADDR]<<8) | (BASE)[(ADDR)+1]) 9 | 10 | #define READ_WORD_LONG(BASE, ADDR) (((BASE)[(ADDR)+1]<<24) | \ 11 | ((BASE)[(ADDR)]<<16) | \ 12 | ((BASE)[(ADDR)+3]<<8) | \ 13 | (BASE)[(ADDR)+2]) 14 | 15 | #define WRITE_BYTE(BASE, ADDR, VAL) (BASE)[(ADDR)^1] = (VAL)&0xff 16 | 17 | #define WRITE_WORD(BASE, ADDR, VAL) (BASE)[ADDR] = ((VAL)>>8) & 0xff; \ 18 | (BASE)[(ADDR)+1] = (VAL)&0xff 19 | 20 | #define WRITE_WORD_LONG(BASE, ADDR, VAL) (BASE)[(ADDR+1)] = ((VAL)>>24) & 0xff; \ 21 | (BASE)[(ADDR)] = ((VAL)>>16)&0xff; \ 22 | (BASE)[(ADDR+3)] = ((VAL)>>8)&0xff; \ 23 | (BASE)[(ADDR+2)] = (VAL)&0xff 24 | 25 | #else 26 | 27 | #define READ_BYTE(BASE, ADDR) (BASE)[ADDR] 28 | #define READ_WORD(BASE, ADDR) *(uint16 *)((BASE) + (ADDR)) 29 | #define READ_WORD_LONG(BASE, ADDR) *(uint32 *)((BASE) + (ADDR)) 30 | #define WRITE_BYTE(BASE, ADDR, VAL) (BASE)[ADDR] = VAL & 0xff 31 | #define WRITE_WORD(BASE, ADDR, VAL) *(uint16 *)((BASE) + (ADDR)) = VAL & 0xffff 32 | #define WRITE_WORD_LONG(BASE, ADDR, VAL) *(uint32 *)((BASE) + (ADDR)) = VAL & 0xffffffff 33 | #endif 34 | 35 | /* C89 compatibility */ 36 | #ifndef M_PI 37 | #define M_PI 3.14159265358979323846264338327f 38 | #endif /* M_PI */ 39 | 40 | /* Set to your compiler's static inline keyword to enable it, or 41 | * set it to blank to disable it. 42 | * If you define INLINE in makefile or osd.h, it will override this value. 43 | * NOTE: not enabling inline functions will SEVERELY slow down emulation. 44 | */ 45 | #ifndef INLINE 46 | #define INLINE static __inline__ 47 | #endif /* INLINE */ 48 | 49 | /* Alignment macros for cross compiler compatibility */ 50 | #if defined(_MSC_VER) 51 | #define ALIGNED_(x) __declspec(align(x)) 52 | #elif defined(__GNUC__) 53 | #define ALIGNED_(x) __attribute__ ((aligned(x))) 54 | #endif 55 | 56 | /* Default CD image file access (read-only) functions */ 57 | /* If you need to override default stdio.h functions with custom filesystem API, 58 | redefine following macros in platform specific include file (osd.h) or Makefile 59 | */ 60 | #ifndef cdStream 61 | #define cdStream FILE 62 | #define cdStreamOpen(fname) fopen(fname, "rb") 63 | #define cdStreamClose fclose 64 | #define cdStreamRead fread 65 | #define cdStreamSeek fseek 66 | #define cdStreamTell ftell 67 | #define cdStreamGets fgets 68 | #endif 69 | 70 | #endif /* _MACROS_H_ */ 71 | -------------------------------------------------------------------------------- /src/gwenesis/cpus/Z80/CodesED.h: -------------------------------------------------------------------------------- 1 | /** Z80: portable Z80 emulator *******************************/ 2 | /** **/ 3 | /** CodesED.h **/ 4 | /** **/ 5 | /** This file contains implementation for the ED table of **/ 6 | /** Z80 commands. It is included from Z80.c. **/ 7 | /** **/ 8 | /** Copyright (C) Marat Fayzullin 1994-2007 **/ 9 | /** You are not allowed to distribute this software **/ 10 | /** commercially. Please, notify me, if you make any **/ 11 | /** changes to this file. **/ 12 | /*************************************************************/ 13 | 14 | /** This is a special patch for emulating BIOS calls: ********/ 15 | case DB_FE: PatchZ80(R);break; 16 | /*************************************************************/ 17 | 18 | case ADC_HL_BC: M_ADCW(BC);break; 19 | case ADC_HL_DE: M_ADCW(DE);break; 20 | case ADC_HL_HL: M_ADCW(HL);break; 21 | case ADC_HL_SP: M_ADCW(SP);break; 22 | 23 | case SBC_HL_BC: M_SBCW(BC);break; 24 | case SBC_HL_DE: M_SBCW(DE);break; 25 | case SBC_HL_HL: M_SBCW(HL);break; 26 | case SBC_HL_SP: M_SBCW(SP);break; 27 | 28 | case LD_xWORDe_HL: 29 | J.B.l=OpZ80(R->PC.W++); 30 | J.B.h=OpZ80(R->PC.W++); 31 | WrZ80(J.W++,R->HL.B.l); 32 | WrZ80(J.W,R->HL.B.h); 33 | break; 34 | case LD_xWORDe_DE: 35 | J.B.l=OpZ80(R->PC.W++); 36 | J.B.h=OpZ80(R->PC.W++); 37 | WrZ80(J.W++,R->DE.B.l); 38 | WrZ80(J.W,R->DE.B.h); 39 | break; 40 | case LD_xWORDe_BC: 41 | J.B.l=OpZ80(R->PC.W++); 42 | J.B.h=OpZ80(R->PC.W++); 43 | WrZ80(J.W++,R->BC.B.l); 44 | WrZ80(J.W,R->BC.B.h); 45 | break; 46 | case LD_xWORDe_SP: 47 | J.B.l=OpZ80(R->PC.W++); 48 | J.B.h=OpZ80(R->PC.W++); 49 | WrZ80(J.W++,R->SP.B.l); 50 | WrZ80(J.W,R->SP.B.h); 51 | break; 52 | 53 | case LD_HL_xWORDe: 54 | J.B.l=OpZ80(R->PC.W++); 55 | J.B.h=OpZ80(R->PC.W++); 56 | R->HL.B.l=RdZ80(J.W++); 57 | R->HL.B.h=RdZ80(J.W); 58 | break; 59 | case LD_DE_xWORDe: 60 | J.B.l=OpZ80(R->PC.W++); 61 | J.B.h=OpZ80(R->PC.W++); 62 | R->DE.B.l=RdZ80(J.W++); 63 | R->DE.B.h=RdZ80(J.W); 64 | break; 65 | case LD_BC_xWORDe: 66 | J.B.l=OpZ80(R->PC.W++); 67 | J.B.h=OpZ80(R->PC.W++); 68 | R->BC.B.l=RdZ80(J.W++); 69 | R->BC.B.h=RdZ80(J.W); 70 | break; 71 | case LD_SP_xWORDe: 72 | J.B.l=OpZ80(R->PC.W++); 73 | J.B.h=OpZ80(R->PC.W++); 74 | R->SP.B.l=RdZ80(J.W++); 75 | R->SP.B.h=RdZ80(J.W); 76 | break; 77 | 78 | case RRD: 79 | I=RdZ80(R->HL.W); 80 | J.B.l=(I>>4)|(R->AF.B.h<<4); 81 | WrZ80(R->HL.W,J.B.l); 82 | R->AF.B.h=(I&0x0F)|(R->AF.B.h&0xF0); 83 | R->AF.B.l=PZSTable[R->AF.B.h]|(R->AF.B.l&C_FLAG); 84 | break; 85 | case RLD: 86 | I=RdZ80(R->HL.W); 87 | J.B.l=(I<<4)|(R->AF.B.h&0x0F); 88 | WrZ80(R->HL.W,J.B.l); 89 | R->AF.B.h=(I>>4)|(R->AF.B.h&0xF0); 90 | R->AF.B.l=PZSTable[R->AF.B.h]|(R->AF.B.l&C_FLAG); 91 | break; 92 | 93 | case LD_A_I: 94 | R->AF.B.h=R->I; 95 | R->AF.B.l=(R->AF.B.l&C_FLAG)|(R->IFF&IFF_2? P_FLAG:0)|ZSTable[R->AF.B.h]; 96 | break; 97 | 98 | case LD_A_R: 99 | R->R++; 100 | R->AF.B.h=(byte)(R->R-R->ICount); 101 | R->AF.B.l=(R->AF.B.l&C_FLAG)|(R->IFF&IFF_2? P_FLAG:0)|ZSTable[R->AF.B.h]; 102 | break; 103 | 104 | case LD_I_A: R->I=R->AF.B.h;break; 105 | case LD_R_A: break; 106 | 107 | case IM_0: R->IFF&=~(IFF_IM1|IFF_IM2);break; 108 | case IM_1: R->IFF=(R->IFF&~IFF_IM2)|IFF_IM1;break; 109 | case IM_2: R->IFF=(R->IFF&~IFF_IM1)|IFF_IM2;break; 110 | 111 | case RETI: 112 | case RETN: if(R->IFF&IFF_2) R->IFF|=IFF_1; else R->IFF&=~IFF_1; 113 | M_RET;break; 114 | 115 | case NEG: I=R->AF.B.h;R->AF.B.h=0;M_SUB(I);break; 116 | 117 | case IN_B_xC: M_IN(R->BC.B.h);break; 118 | case IN_C_xC: M_IN(R->BC.B.l);break; 119 | case IN_D_xC: M_IN(R->DE.B.h);break; 120 | case IN_E_xC: M_IN(R->DE.B.l);break; 121 | case IN_H_xC: M_IN(R->HL.B.h);break; 122 | case IN_L_xC: M_IN(R->HL.B.l);break; 123 | case IN_A_xC: M_IN(R->AF.B.h);break; 124 | case IN_F_xC: M_IN(J.B.l);break; 125 | 126 | case OUT_xC_B: OutZ80(R->BC.W,R->BC.B.h);break; 127 | case OUT_xC_C: OutZ80(R->BC.W,R->BC.B.l);break; 128 | case OUT_xC_D: OutZ80(R->BC.W,R->DE.B.h);break; 129 | case OUT_xC_E: OutZ80(R->BC.W,R->DE.B.l);break; 130 | case OUT_xC_H: OutZ80(R->BC.W,R->HL.B.h);break; 131 | case OUT_xC_L: OutZ80(R->BC.W,R->HL.B.l);break; 132 | case OUT_xC_A: OutZ80(R->BC.W,R->AF.B.h);break; 133 | 134 | case INI: 135 | WrZ80(R->HL.W++,InZ80(R->BC.W)); 136 | --R->BC.B.h; 137 | R->AF.B.l=N_FLAG|(R->BC.B.h? 0:Z_FLAG); 138 | break; 139 | 140 | case INIR: 141 | do 142 | { 143 | WrZ80(R->HL.W++,InZ80(R->BC.W)); 144 | --R->BC.B.h;R->ICount-=21; 145 | } 146 | while(R->BC.B.h&&(R->ICount>0)); 147 | if(R->BC.B.h) { R->AF.B.l=N_FLAG;R->PC.W-=2; } 148 | else { R->AF.B.l=Z_FLAG|N_FLAG;R->ICount+=5; } 149 | break; 150 | 151 | case IND: 152 | WrZ80(R->HL.W--,InZ80(R->BC.W)); 153 | --R->BC.B.h; 154 | R->AF.B.l=N_FLAG|(R->BC.B.h? 0:Z_FLAG); 155 | break; 156 | 157 | case INDR: 158 | do 159 | { 160 | WrZ80(R->HL.W--,InZ80(R->BC.W)); 161 | --R->BC.B.h;R->ICount-=21; 162 | } 163 | while(R->BC.B.h&&(R->ICount>0)); 164 | if(R->BC.B.h) { R->AF.B.l=N_FLAG;R->PC.W-=2; } 165 | else { R->AF.B.l=Z_FLAG|N_FLAG;R->ICount+=5; } 166 | break; 167 | 168 | case OUTI: 169 | --R->BC.B.h; 170 | I=RdZ80(R->HL.W++); 171 | OutZ80(R->BC.W,I); 172 | R->AF.B.l=N_FLAG|(R->BC.B.h? 0:Z_FLAG)|(R->HL.B.l+I>255? (C_FLAG|H_FLAG):0); 173 | break; 174 | 175 | case OTIR: 176 | do 177 | { 178 | --R->BC.B.h; 179 | I=RdZ80(R->HL.W++); 180 | OutZ80(R->BC.W,I); 181 | R->ICount-=21; 182 | } 183 | while(R->BC.B.h&&(R->ICount>0)); 184 | if(R->BC.B.h) 185 | { 186 | R->AF.B.l=N_FLAG|(R->HL.B.l+I>255? (C_FLAG|H_FLAG):0); 187 | R->PC.W-=2; 188 | } 189 | else 190 | { 191 | R->AF.B.l=Z_FLAG|N_FLAG|(R->HL.B.l+I>255? (C_FLAG|H_FLAG):0); 192 | R->ICount+=5; 193 | } 194 | break; 195 | 196 | case OUTD: 197 | --R->BC.B.h; 198 | I=RdZ80(R->HL.W--); 199 | OutZ80(R->BC.W,I); 200 | R->AF.B.l=N_FLAG|(R->BC.B.h? 0:Z_FLAG)|(R->HL.B.l+I>255? (C_FLAG|H_FLAG):0); 201 | break; 202 | 203 | case OTDR: 204 | do 205 | { 206 | --R->BC.B.h; 207 | I=RdZ80(R->HL.W--); 208 | OutZ80(R->BC.W,I); 209 | R->ICount-=21; 210 | } 211 | while(R->BC.B.h&&(R->ICount>0)); 212 | if(R->BC.B.h) 213 | { 214 | R->AF.B.l=N_FLAG|(R->HL.B.l+I>255? (C_FLAG|H_FLAG):0); 215 | R->PC.W-=2; 216 | } 217 | else 218 | { 219 | R->AF.B.l=Z_FLAG|N_FLAG|(R->HL.B.l+I>255? (C_FLAG|H_FLAG):0); 220 | R->ICount+=5; 221 | } 222 | break; 223 | 224 | case LDI: 225 | WrZ80(R->DE.W++,RdZ80(R->HL.W++)); 226 | --R->BC.W; 227 | R->AF.B.l=(R->AF.B.l&~(N_FLAG|H_FLAG|P_FLAG))|(R->BC.W? P_FLAG:0); 228 | break; 229 | 230 | case LDIR: 231 | do 232 | { 233 | WrZ80(R->DE.W++,RdZ80(R->HL.W++)); 234 | --R->BC.W;R->ICount-=21; 235 | } 236 | while(R->BC.W&&(R->ICount>0)); 237 | R->AF.B.l&=~(N_FLAG|H_FLAG|P_FLAG); 238 | if(R->BC.W) { R->AF.B.l|=N_FLAG;R->PC.W-=2; } 239 | else R->ICount+=5; 240 | break; 241 | 242 | case LDD: 243 | WrZ80(R->DE.W--,RdZ80(R->HL.W--)); 244 | --R->BC.W; 245 | R->AF.B.l=(R->AF.B.l&~(N_FLAG|H_FLAG|P_FLAG))|(R->BC.W? P_FLAG:0); 246 | break; 247 | 248 | case LDDR: 249 | do 250 | { 251 | WrZ80(R->DE.W--,RdZ80(R->HL.W--)); 252 | --R->BC.W;R->ICount-=21; 253 | } 254 | while(R->BC.W&&(R->ICount>0)); 255 | R->AF.B.l&=~(N_FLAG|H_FLAG|P_FLAG); 256 | if(R->BC.W) { R->AF.B.l|=N_FLAG;R->PC.W-=2; } 257 | else R->ICount+=5; 258 | break; 259 | 260 | case CPI: 261 | I=RdZ80(R->HL.W++); 262 | J.B.l=R->AF.B.h-I; 263 | --R->BC.W; 264 | R->AF.B.l = 265 | N_FLAG|(R->AF.B.l&C_FLAG)|ZSTable[J.B.l]| 266 | ((R->AF.B.h^I^J.B.l)&H_FLAG)|(R->BC.W? P_FLAG:0); 267 | break; 268 | 269 | case CPIR: 270 | do 271 | { 272 | I=RdZ80(R->HL.W++); 273 | J.B.l=R->AF.B.h-I; 274 | --R->BC.W;R->ICount-=21; 275 | } 276 | while(R->BC.W&&J.B.l&&(R->ICount>0)); 277 | R->AF.B.l = 278 | N_FLAG|(R->AF.B.l&C_FLAG)|ZSTable[J.B.l]| 279 | ((R->AF.B.h^I^J.B.l)&H_FLAG)|(R->BC.W? P_FLAG:0); 280 | if(R->BC.W&&J.B.l) R->PC.W-=2; else R->ICount+=5; 281 | break; 282 | 283 | case CPD: 284 | I=RdZ80(R->HL.W--); 285 | J.B.l=R->AF.B.h-I; 286 | --R->BC.W; 287 | R->AF.B.l = 288 | N_FLAG|(R->AF.B.l&C_FLAG)|ZSTable[J.B.l]| 289 | ((R->AF.B.h^I^J.B.l)&H_FLAG)|(R->BC.W? P_FLAG:0); 290 | break; 291 | 292 | case CPDR: 293 | do 294 | { 295 | I=RdZ80(R->HL.W--); 296 | J.B.l=R->AF.B.h-I; 297 | --R->BC.W;R->ICount-=21; 298 | } 299 | while(R->BC.W&&J.B.l); 300 | R->AF.B.l = 301 | N_FLAG|(R->AF.B.l&C_FLAG)|ZSTable[J.B.l]| 302 | ((R->AF.B.h^I^J.B.l)&H_FLAG)|(R->BC.W? P_FLAG:0); 303 | if(R->BC.W&&J.B.l) R->PC.W-=2; else R->ICount+=5; 304 | break; 305 | -------------------------------------------------------------------------------- /src/gwenesis/cpus/Z80/CodesXCB.h: -------------------------------------------------------------------------------- 1 | /** Z80: portable Z80 emulator *******************************/ 2 | /** **/ 3 | /** CodesXCB.h **/ 4 | /** **/ 5 | /** This file contains implementation for FD/DD-CB tables **/ 6 | /** of Z80 commands. It is included from Z80.c. **/ 7 | /** **/ 8 | /** Copyright (C) Marat Fayzullin 1994-2007 **/ 9 | /** You are not allowed to distribute this software **/ 10 | /** commercially. Please, notify me, if you make any **/ 11 | /** changes to this file. **/ 12 | /*************************************************************/ 13 | 14 | case RLC_xHL: I=RdZ80(J.W);M_RLC(I);WrZ80(J.W,I);break; 15 | case RRC_xHL: I=RdZ80(J.W);M_RRC(I);WrZ80(J.W,I);break; 16 | case RL_xHL: I=RdZ80(J.W);M_RL(I);WrZ80(J.W,I);break; 17 | case RR_xHL: I=RdZ80(J.W);M_RR(I);WrZ80(J.W,I);break; 18 | case SLA_xHL: I=RdZ80(J.W);M_SLA(I);WrZ80(J.W,I);break; 19 | case SRA_xHL: I=RdZ80(J.W);M_SRA(I);WrZ80(J.W,I);break; 20 | case SLL_xHL: I=RdZ80(J.W);M_SLL(I);WrZ80(J.W,I);break; 21 | case SRL_xHL: I=RdZ80(J.W);M_SRL(I);WrZ80(J.W,I);break; 22 | 23 | case BIT0_B: case BIT0_C: case BIT0_D: case BIT0_E: 24 | case BIT0_H: case BIT0_L: case BIT0_A: 25 | case BIT0_xHL: I=RdZ80(J.W);M_BIT(0,I);break; 26 | case BIT1_B: case BIT1_C: case BIT1_D: case BIT1_E: 27 | case BIT1_H: case BIT1_L: case BIT1_A: 28 | case BIT1_xHL: I=RdZ80(J.W);M_BIT(1,I);break; 29 | case BIT2_B: case BIT2_C: case BIT2_D: case BIT2_E: 30 | case BIT2_H: case BIT2_L: case BIT2_A: 31 | case BIT2_xHL: I=RdZ80(J.W);M_BIT(2,I);break; 32 | case BIT3_B: case BIT3_C: case BIT3_D: case BIT3_E: 33 | case BIT3_H: case BIT3_L: case BIT3_A: 34 | case BIT3_xHL: I=RdZ80(J.W);M_BIT(3,I);break; 35 | case BIT4_B: case BIT4_C: case BIT4_D: case BIT4_E: 36 | case BIT4_H: case BIT4_L: case BIT4_A: 37 | case BIT4_xHL: I=RdZ80(J.W);M_BIT(4,I);break; 38 | case BIT5_B: case BIT5_C: case BIT5_D: case BIT5_E: 39 | case BIT5_H: case BIT5_L: case BIT5_A: 40 | case BIT5_xHL: I=RdZ80(J.W);M_BIT(5,I);break; 41 | case BIT6_B: case BIT6_C: case BIT6_D: case BIT6_E: 42 | case BIT6_H: case BIT6_L: case BIT6_A: 43 | case BIT6_xHL: I=RdZ80(J.W);M_BIT(6,I);break; 44 | case BIT7_B: case BIT7_C: case BIT7_D: case BIT7_E: 45 | case BIT7_H: case BIT7_L: case BIT7_A: 46 | case BIT7_xHL: I=RdZ80(J.W);M_BIT(7,I);break; 47 | 48 | case RES0_xHL: I=RdZ80(J.W);M_RES(0,I);WrZ80(J.W,I);break; 49 | case RES1_xHL: I=RdZ80(J.W);M_RES(1,I);WrZ80(J.W,I);break; 50 | case RES2_xHL: I=RdZ80(J.W);M_RES(2,I);WrZ80(J.W,I);break; 51 | case RES3_xHL: I=RdZ80(J.W);M_RES(3,I);WrZ80(J.W,I);break; 52 | case RES4_xHL: I=RdZ80(J.W);M_RES(4,I);WrZ80(J.W,I);break; 53 | case RES5_xHL: I=RdZ80(J.W);M_RES(5,I);WrZ80(J.W,I);break; 54 | case RES6_xHL: I=RdZ80(J.W);M_RES(6,I);WrZ80(J.W,I);break; 55 | case RES7_xHL: I=RdZ80(J.W);M_RES(7,I);WrZ80(J.W,I);break; 56 | 57 | case SET0_xHL: I=RdZ80(J.W);M_SET(0,I);WrZ80(J.W,I);break; 58 | case SET1_xHL: I=RdZ80(J.W);M_SET(1,I);WrZ80(J.W,I);break; 59 | case SET2_xHL: I=RdZ80(J.W);M_SET(2,I);WrZ80(J.W,I);break; 60 | case SET3_xHL: I=RdZ80(J.W);M_SET(3,I);WrZ80(J.W,I);break; 61 | case SET4_xHL: I=RdZ80(J.W);M_SET(4,I);WrZ80(J.W,I);break; 62 | case SET5_xHL: I=RdZ80(J.W);M_SET(5,I);WrZ80(J.W,I);break; 63 | case SET6_xHL: I=RdZ80(J.W);M_SET(6,I);WrZ80(J.W,I);break; 64 | case SET7_xHL: I=RdZ80(J.W);M_SET(7,I);WrZ80(J.W,I);break; 65 | -------------------------------------------------------------------------------- /src/gwenesis/cpus/Z80/ConDebug.c: -------------------------------------------------------------------------------- 1 | /** Z80: portable Z80 emulator *******************************/ 2 | /** **/ 3 | /** ConDebug.c **/ 4 | /** **/ 5 | /** This file contains a console version of the built-in **/ 6 | /** debugger, using EMULib's Console.c. When -DCONDEBUG is **/ 7 | /** ommitted, ConDebug.c just includes the default command **/ 8 | /** line based debugger (Debug.c). **/ 9 | /** **/ 10 | /** Copyright (C) Marat Fayzullin 2005-2007 **/ 11 | /** You are not allowed to distribute this software **/ 12 | /** commercially. Please, notify me, if you make any **/ 13 | /** changes to this file. **/ 14 | /*************************************************************/ 15 | #pragma GCC optimize("Ofast") 16 | 17 | #ifdef DEBUG 18 | 19 | #ifndef CONDEBUG 20 | /** Normal DebugZ80() ****************************************/ 21 | /** When CONDEBUG #undefined, we use plain command line. **/ 22 | /*************************************************************/ 23 | #include "Debug.c" 24 | 25 | #else 26 | /** Console DebugZ80() ***************************************/ 27 | /** When CONDEBUG #defined, we use EMULib console. **/ 28 | /*************************************************************/ 29 | 30 | #include "Z80.h" 31 | #include "Console.h" 32 | #include 33 | 34 | #define DebugZ80 OriginalDebugZ80 35 | #include "Debug.c" 36 | #undef DebugZ80 37 | 38 | #define CLR_BACK PIXEL(255,255,255) 39 | #define CLR_TEXT PIXEL(0,0,0) 40 | #define CLR_DIALOG PIXEL(0,100,0) 41 | #define CLR_PC PIXEL(255,0,0) 42 | #define CLR_SP PIXEL(0,0,100) 43 | 44 | static byte ChrDump(byte C) 45 | { 46 | return((C>=32)&&(C<128)? C:'.'); 47 | } 48 | 49 | /** DebugZ80() ***********************************************/ 50 | /** This function should exist if DEBUG is #defined. When **/ 51 | /** Trace!=0, it is called after each command executed by **/ 52 | /** the CPU, and given the Z80 registers. **/ 53 | /*************************************************************/ 54 | byte DebugZ80(Z80 *R) 55 | { 56 | char S[1024]; 57 | word A,Addr,ABuf[20]; 58 | int J,I,K,X,Y,MemoryDump,DrawWindow,ExitNow; 59 | 60 | /* If we don't have enough screen estate... */ 61 | if((VideoW<32*8)||(VideoH<23*8)) 62 | { 63 | /* Show warning message */ 64 | CONMsg( 65 | -1,-1,-1,-1,PIXEL(255,255,255),PIXEL(255,0,0), 66 | "Error","Screen is\0too small!\0\0" 67 | ); 68 | /* Continue emulation */ 69 | R->Trace=0; 70 | return(1); 71 | } 72 | 73 | X = ((VideoW>>3)-32)>>1; 74 | Y = ((VideoH>>3)-23)>>1; 75 | Addr = R->PC.W; 76 | A = ~Addr; 77 | K = 0; 78 | 79 | for(DrawWindow=1,MemoryDump=ExitNow=0;!ExitNow&&VideoImg;) 80 | { 81 | if(DrawWindow) 82 | { 83 | CONWindow(X,Y,32,23,CLR_TEXT,CLR_BACK,"Z80 Debugger"); 84 | 85 | sprintf(S,"PC %04X",R->PC.W); 86 | CONSetColor(CLR_BACK,CLR_PC); 87 | CONPrint(X+24,Y+18,S); 88 | sprintf(S,"SP %04X",R->SP.W); 89 | CONSetColor(CLR_BACK,CLR_SP); 90 | CONPrint(X+24,Y+19,S); 91 | 92 | CONSetColor(CLR_TEXT,CLR_BACK); 93 | sprintf(S, 94 | " %c%c%c%c%c%c\n\n" 95 | "AF %04X\nBC %04X\nDE %04X\nHL %04X\nIX %04X\nIY %04X\n\n" 96 | "AF'%04X\nBC'%04X\nDE'%04X\nHL'%04X\n\n" 97 | "IR %02X%02X", 98 | R->AF.B.l&0x80? 'S':'.',R->AF.B.l&0x40? 'Z':'.',R->AF.B.l&0x10? 'H':'.', 99 | R->AF.B.l&0x04? 'P':'.',R->AF.B.l&0x02? 'N':'.',R->AF.B.l&0x01? 'C':'.', 100 | R->AF.W,R->BC.W,R->DE.W,R->HL.W, 101 | R->IX.W,R->IY.W, 102 | R->AF1.W,R->BC1.W,R->DE1.W,R->HL1.W, 103 | R->I,R->R 104 | ); 105 | CONPrint(X+24,Y+2,S); 106 | sprintf(S, 107 | "%s %s", 108 | R->IFF&0x04? "IM2":R->IFF&0x02? "IM1":"IM0", 109 | R->IFF&0x01? "EI":"DI" 110 | ); 111 | CONPrint(X+25,Y+21,S); 112 | DrawWindow=0; 113 | A=~Addr; 114 | } 115 | 116 | /* If top address has changed... */ 117 | if(A!=Addr) 118 | { 119 | /* Clear display */ 120 | CONBox((X+1)<<3,(Y+2)<<3,23*8,20*8,CLR_BACK); 121 | 122 | if(MemoryDump) 123 | { 124 | /* Draw memory dump */ 125 | for(J=0,A=Addr;J<20;J++,A+=4) 126 | { 127 | if(A==R->PC.W) CONSetColor(CLR_BACK,CLR_PC); 128 | else if(A==R->SP.W) CONSetColor(CLR_BACK,CLR_SP); 129 | else CONSetColor(CLR_TEXT,CLR_BACK); 130 | sprintf(S,"%04X%c",A,A==R->PC.W? CON_MORE:A==R->SP.W? CON_LESS:':'); 131 | CONPrint(X+1,Y+J+2,S); 132 | 133 | CONSetColor(CLR_TEXT,CLR_BACK); 134 | sprintf(S, 135 | "%02X %02X %02X %02X %c%c%c%c", 136 | RdZ80(A),RdZ80(A+1),RdZ80(A+2),RdZ80(A+3), 137 | ChrDump(RdZ80(A)),ChrDump(RdZ80(A+1)), 138 | ChrDump(RdZ80(A+2)),ChrDump(RdZ80(A+3)) 139 | ); 140 | CONPrint(X+7,Y+J+2,S); 141 | } 142 | } 143 | else 144 | { 145 | /* Draw listing */ 146 | for(J=0,A=Addr;J<20;J++) 147 | { 148 | if(A==R->PC.W) CONSetColor(CLR_BACK,CLR_PC); 149 | else if(A==R->SP.W) CONSetColor(CLR_BACK,CLR_SP); 150 | else CONSetColor(CLR_TEXT,CLR_BACK); 151 | sprintf(S,"%04X%c",A,A==R->PC.W? CON_MORE:A==R->SP.W? CON_LESS:':'); 152 | CONPrint(X+1,Y+J+2,S); 153 | 154 | ABuf[J]=A; 155 | A+=DAsm(S,A); 156 | 157 | CONSetColor(CLR_TEXT,CLR_BACK); 158 | CONPrintN(X+7,Y+J+2,S,23); 159 | } 160 | } 161 | 162 | /* Display redrawn */ 163 | A=Addr; 164 | } 165 | 166 | /* Draw pointer */ 167 | CONChar(X+6,Y+K+2,CON_ARROW); 168 | 169 | /* Show screen buffer */ 170 | ShowVideo(); 171 | 172 | /* Get key code */ 173 | I=WaitKey(); 174 | 175 | /* Clear pointer */ 176 | CONChar(X+6,Y+K+2,' '); 177 | 178 | /* Get and process key code */ 179 | switch(I) 180 | { 181 | case 'H': 182 | CONMsg( 183 | -1,-1,-1,-1, 184 | CLR_BACK,CLR_DIALOG, 185 | "Debugger Help", 186 | "ENTER - Execute next opcode\0" 187 | " UP - Previous opcode\0" 188 | " DOWN - Next opcode\0" 189 | " LEFT - Page up\0" 190 | "RIGHT - Page down\0" 191 | " H - This help page\0" 192 | " G - Go to address\0" 193 | " D - Disassembler view\0" 194 | " M - Memory dump view\0" 195 | " S - Show stack\0" 196 | " J - Jump to cursor\0" 197 | " R - Run to cursor\0" 198 | " C - Continue execution\0" 199 | " Q - Quit emulator\0" 200 | ); 201 | DrawWindow=1; 202 | break; 203 | case CON_UP: 204 | if(K) --K; 205 | else 206 | if(MemoryDump) Addr-=4; 207 | else for(--Addr;Addr+DAsm(S,Addr)>A;--Addr); 208 | break; 209 | case CON_DOWN: 210 | if(K<19) ++K; 211 | else 212 | if(MemoryDump) Addr+=4; 213 | else Addr+=DAsm(S,Addr); 214 | break; 215 | case CON_LEFT: 216 | if(MemoryDump) 217 | Addr-=4*20; 218 | else 219 | { 220 | for(I=20,Addr=~A;(Addr>A)||((A^Addr)&~Addr&0x8000);++I) 221 | for(J=0,Addr=A-I;J<20;++J) Addr+=DAsm(S,Addr); 222 | Addr=A-I+1; 223 | } 224 | break; 225 | case CON_RIGHT: 226 | if(MemoryDump) 227 | Addr+=4*20; 228 | else 229 | for(J=0;J<20;++J) Addr+=DAsm(S,Addr); 230 | break; 231 | case CON_OK: 232 | ExitNow=1; 233 | break; 234 | case 'Q': 235 | return(0); 236 | case CON_EXIT: 237 | case 'C': 238 | R->Trap=0xFFFF; 239 | R->Trace=0; 240 | ExitNow=1; 241 | break; 242 | case 'R': 243 | R->Trap=ABuf[K]; 244 | R->Trace=0; 245 | ExitNow=1; 246 | break; 247 | case 'M': 248 | MemoryDump=1; 249 | A=~Addr; 250 | break; 251 | case 'S': 252 | MemoryDump=1; 253 | Addr=R->SP.W; 254 | K=0; 255 | A=~Addr; 256 | break; 257 | case 'D': 258 | MemoryDump=0; 259 | A=~Addr; 260 | break; 261 | case 'G': 262 | if(CONInput(-1,-1,CLR_BACK,CLR_DIALOG,"Go to Address:",S,5|CON_HEX)) 263 | { Addr=strtol(S,0,16);K=0; } 264 | DrawWindow=1; 265 | break; 266 | case 'J': 267 | R->PC.W=ABuf[K]; 268 | A=~Addr; 269 | break; 270 | } 271 | } 272 | 273 | /* Continue emulation */ 274 | return(1); 275 | } 276 | 277 | #endif /* CONDEBUG */ 278 | #endif /* DEBUG */ 279 | -------------------------------------------------------------------------------- /src/gwenesis/io/gwenesis_io.c: -------------------------------------------------------------------------------- 1 | /* 2 | Gwenesis : Genesis & megadrive Emulator. 3 | 4 | This program is free software: you can redistribute it and/or modify it under 5 | the terms of the GNU General Public License as published by the Free Software 6 | Foundation, either version 3 of the License, or (at your option) any later 7 | version. 8 | This program is distributed in the hope that it will be useful, but WITHOUT 9 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 10 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 11 | You should have received a copy of the GNU General Public License along with 12 | this program. If not, see . 13 | 14 | __author__ = "bzhxx" 15 | __contact__ = "https://github.com/bzhxx" 16 | __license__ = "GPLv3" 17 | 18 | */ 19 | #pragma GCC optimize("Ofast") 20 | 21 | #include 22 | #include 23 | #include 24 | #include "gwenesis_io.h" 25 | #include "../savestate/gwenesis_savestate.h" 26 | 27 | unsigned short button_state[3]= {0xff,0xff,0xff}; 28 | 29 | /* Button mapping 30 | 7 6 5 4 3 2 1 0 31 | Start A B C R L D U 32 | */ 33 | static unsigned char gwenesis_io_pad_state[3] = {0x33,0x33,0x33}; 34 | 35 | //#define GWENESIS_IO_VERSION 0x81 /* oversea NTSC model version 81 */ 36 | #define GWENESIS_IO_VERSION 0xC1 /* oversea PAL model version C1 */ 37 | /* 38 | $A10003 : MODE VMOD DISK RSV VER3 VER2 VER1 VER0 39 | MODE (R) 0: Domestic Model 40 | 1: Overseas Model 41 | VMOD (R) 0: NTSC CPU clock 7.67 MHz 42 | 1: PAL CPU clock 7.60 MHz 43 | RSV (R) Currently not used 44 | VER3-0 (R) MEGA DRIVE version is indicated by $0-$F. The present hardware version is indicated by $0. 45 | $A10003 : DATA 1 ( CTRL1 ) 46 | $A10005 : DATA 2 ( CTRL2 ) 47 | $A10007 : DATA 3 ( EXP ) 48 | $A10009 : CTRL 1 49 | $A1000B : CTRL 2 50 | $A1000D : CTRL 3 51 | $A1000F : TxDATA 1 52 | $A10011 : RxDATA 1 53 | $A10013 : S-CTRL 1 54 | $A10015 : TxDATA 2 55 | $A10017 : RxDATA 2 56 | $A10019 : S-CTRL 2 57 | $A1001B : TxDATA 3 58 | $A1001D : RxDATA 3 59 | $A1001F : S-CTRL 3 60 | 61 | DATA shows the status of each port. The I/O direction of each bit is set by CTRL and S-CTRL. 62 | DATA = PD7 PD6 PD5 PD4 PD3 PD2 PD1 PD0 63 | 64 | PD7 (RW) 65 | PD6 (RW) TH 66 | PD5 (RW) TR 67 | PD4 (RW) TL 68 | PD3 (RW) RIGHT 69 | PD2 (RW) LEFT 70 | PD1 (RW) DOWN 71 | PD0 (RW) UP 72 | 73 | TH = 0 74 | PD7 0 75 | PD6 TH=0 76 | PD5 Start 77 | PD4 A 78 | PD3 0 79 | PD2 0 80 | PD1 DOWN 81 | PD0 UP 82 | 83 | TH = 1 84 | PD7 0 85 | PD6 TH=1 86 | PD5 B 87 | PD4 C 88 | PD3 RIGHT 89 | PD2 LEFT 90 | PD1 DOWN 91 | PD0 UP 92 | 93 | 94 | CTRL designates the I/O direction of each port and the INTERRUPT CONTROL of TH. 95 | CTRL = INT PC6 PC5 PC4 PC3 PC2 PC1 PC0 96 | 97 | INT (RW) 0: TH-INT PROHIBITED 98 | 1: TH-INT ALLOWED 99 | PC6 (RW) 0: PD6 INPUT MODE 100 | 1: OUTPUT MODE 101 | PC5 (RW) 0: PD5 INPUT MODE 102 | 1: OUTPUT MODE 103 | PC4 (RW) 0: PD4 INPUT MODE 104 | 1: OUTPUT MODE 105 | PC3 (RW) 0: PD3 INPUT MODE 106 | 1: OUTPUT MODE 107 | PC2 (RW) 0: PD2 INPUT MODE 108 | 1: OUTPUT MODE 109 | PC1 (RW) 0: PD1 INPUT MODE 110 | 1: OUTPUT MODE 111 | PC0 (RW) 0: PD0 INPUT MODE 112 | 1: OUTPUT MODE 113 | 114 | S-CTRL is for the status, etc. of each port's mode change, baud rate and SERIAL. 115 | 116 | S-CTRL = BPS1 BPS0 SIN SOUT RINT RERR RRDY TFUL 117 | 118 | SIN (RW) 0: TR-PARALLEL MODE 119 | 1: SERIAL IN 120 | SOUT (RW) 0: TL-PARALLEL MODE 121 | 1: SERIAL OUT 122 | RINT (RW) 0: Rxd READY INTERRUPT PROHIBITED 123 | 1: Rxd READY INTERRUPT ALLOWED 124 | RERR (R) 0: 125 | 1: Rxd ERROR 126 | RRDY (R) 0: 127 | 1: Rxd READY 128 | TFUL (R) 0: 129 | 1: Txd FULL 130 | */ 131 | 132 | unsigned char io_reg[16] = {GWENESIS_IO_VERSION, /* 0x1 Version */ 133 | 0x7f, 0x7f, 0x7f, /* 0x3-5-7 JOYPAD DATA 1 2 & EXT */ 134 | 0x00, 0x00, 0x00, /* 0x9-A-C JOYPAD CTRL 1 2 & EXT */ 135 | 0xff, 0, 0, /* PORT 1 */ 136 | 0xff, 0, 0, /* PORT 2 */ 137 | 0xff, 0, 0}; /* PORT 3 */ 138 | 139 | void gwenesis_io_pad_release_button(int pad, int button) 140 | { 141 | button_state[pad] |= (1 << button); 142 | } 143 | 144 | void gwenesis_io_pad_press_button(int pad, int button) 145 | { 146 | button_state[pad] &= ~(1 << button); 147 | } 148 | 149 | static inline void gwenesis_io_pad_write(int pad, int value) 150 | { 151 | unsigned char mask = io_reg[pad + 4]; 152 | 153 | gwenesis_io_pad_state[pad] &= ~mask; 154 | gwenesis_io_pad_state[pad] |= value & mask; 155 | 156 | } 157 | 158 | static inline unsigned char gwenesis_io_pad_read(int pad) 159 | { 160 | unsigned char value; 161 | 162 | /* get host button */ 163 | gwenesis_io_get_buttons(); 164 | 165 | value = gwenesis_io_pad_state[pad] & 0x40; 166 | value |= 0x3f; 167 | 168 | if (value & 0x40) 169 | { 170 | value &= (button_state[pad] & 0x3f); 171 | } 172 | else 173 | { 174 | value &= ((button_state[pad] & 3) | ((button_state[pad] >> 2) & 0x30)); 175 | } 176 | return value; 177 | } 178 | 179 | void gwenesis_io_write_ctrl(unsigned int address, unsigned int value) 180 | { 181 | 182 | address >>= 1; 183 | 184 | // JOYPAD DATA 185 | if (address >= 0x1 && address <= 0x3) 186 | { 187 | 188 | io_reg[address] = value; 189 | gwenesis_io_pad_write(address - 1, value); 190 | return; 191 | } 192 | // JOYPAD CTRL 193 | else if (address >= 0x4 && address <= 0x6) 194 | { 195 | if (io_reg[address] != value) 196 | { 197 | io_reg[address] = value; 198 | gwenesis_io_pad_write(address - 4, io_reg[address - 3]); 199 | } 200 | return; 201 | } 202 | 203 | return; 204 | } 205 | 206 | unsigned int gwenesis_io_read_ctrl(unsigned int address) 207 | { 208 | address >>= 1; 209 | if (address >= 0x1 && address < 0x4) 210 | { 211 | unsigned char mask = 0x80 | io_reg[address + 3]; 212 | unsigned char value; 213 | value = io_reg[address] & mask; 214 | value |= gwenesis_io_pad_read(address - 1) & ~mask; 215 | 216 | return value; 217 | } 218 | else 219 | { 220 | return io_reg[address]; 221 | } 222 | } 223 | 224 | void gwenesis_io_set_reg(unsigned int reg, unsigned int value) { 225 | io_reg[reg] = value; 226 | return; 227 | } 228 | 229 | void gwenesis_io_save_state() { 230 | SaveState* state; 231 | state = saveGwenesisStateOpenForWrite("io"); 232 | saveGwenesisStateSetBuffer(state, "button_state", button_state, sizeof(button_state)); 233 | saveGwenesisStateSetBuffer(state, "gwenesis_io_pad_state", gwenesis_io_pad_state, sizeof(gwenesis_io_pad_state)); 234 | saveGwenesisStateSetBuffer(state, "io_reg", io_reg, sizeof(io_reg)); 235 | } 236 | 237 | void gwenesis_io_load_state() { 238 | SaveState* state = saveGwenesisStateOpenForRead("io"); 239 | saveGwenesisStateGetBuffer(state, "button_state", button_state, sizeof(button_state)); 240 | saveGwenesisStateGetBuffer(state, "gwenesis_io_pad_state", gwenesis_io_pad_state, sizeof(gwenesis_io_pad_state)); 241 | saveGwenesisStateGetBuffer(state, "io_reg", io_reg, sizeof(io_reg)); 242 | } 243 | -------------------------------------------------------------------------------- /src/gwenesis/io/gwenesis_io.h: -------------------------------------------------------------------------------- 1 | /* 2 | Gwenesis : Genesis & megadrive Emulator. 3 | 4 | This program is free software: you can redistribute it and/or modify it under 5 | the terms of the GNU General Public License as published by the Free Software 6 | Foundation, either version 3 of the License, or (at your option) any later 7 | version. 8 | This program is distributed in the hope that it will be useful, but WITHOUT 9 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 10 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 11 | You should have received a copy of the GNU General Public License along with 12 | this program. If not, see . 13 | 14 | __author__ = "bzhxx" 15 | __contact__ = "https://github.com/bzhxx" 16 | __license__ = "GPLv3" 17 | 18 | */ 19 | #ifndef _gwenesis_io_H_ 20 | #define _gwenesis_io_H_ 21 | 22 | #pragma once 23 | 24 | void gwenesis_io_pad_press_button(int pad, int button); 25 | void gwenesis_io_pad_release_button(int pad, int button); 26 | 27 | void gwenesis_io_write_ctrl(unsigned int address, unsigned int value); 28 | unsigned int gwenesis_io_read_ctrl(unsigned int address); 29 | 30 | void gwenesis_io_set_reg(unsigned int reg, unsigned int value); 31 | void gwenesis_io_get_buttons(); 32 | 33 | void gwenesis_io_save_state(); 34 | void gwenesis_io_load_state(); 35 | 36 | #endif -------------------------------------------------------------------------------- /src/gwenesis/savestate/gwenesis_savestate.c: -------------------------------------------------------------------------------- 1 | /* 2 | Gwenesis : Genesis & megadrive Emulator. 3 | 4 | This program is free software: you can redistribute it and/or modify it under 5 | the terms of the GNU General Public License as published by the Free Software 6 | Foundation, either version 3 of the License, or (at your option) any later 7 | version. 8 | This program is distributed in the hope that it will be useful, but WITHOUT 9 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 10 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 11 | You should have received a copy of the GNU General Public License along with 12 | this program. If not, see . 13 | 14 | __author__ = "bzhxx" 15 | __contact__ = "https://github.com/bzhxx" 16 | __license__ = "GPLv3" 17 | 18 | */ 19 | #pragma GCC optimize("Ofast") 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "../cpus/M68K/m68k.h" 27 | #include "../io/gwenesis_io.h" 28 | #include "../bus/gwenesis_bus.h" 29 | #include "../vdp/gwenesis_vdp.h" 30 | #include "../sound/z80inst.h" 31 | 32 | #include "../savestate/gwenesis_savestate.h" 33 | 34 | #include 35 | 36 | void gwenesis_save_state() { 37 | /* DO NOT CHANGE ORDER - NEEDS TO BE SAME AS IN LOAD */ 38 | gwenesis_m68k_save_state(); 39 | gwenesis_io_save_state(); 40 | gwenesis_bus_save_state(); 41 | gwenesis_vdp_gfx_save_state(); 42 | gwenesis_vdp_mem_save_state(); 43 | 44 | } 45 | 46 | void gwenesis_load_state() { 47 | /* DO NOT CHANGE ORDER - NEEDS TO BE SAME AS IN SAVE */ 48 | gwenesis_m68k_load_state(); 49 | gwenesis_io_load_state(); 50 | gwenesis_bus_load_state(); 51 | gwenesis_vdp_gfx_load_state(); 52 | gwenesis_vdp_mem_load_state(); 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/gwenesis/savestate/gwenesis_savestate.h: -------------------------------------------------------------------------------- 1 | /* 2 | Gwenesis : Genesis & megadrive Emulator. 3 | 4 | This program is free software: you can redistribute it and/or modify it under 5 | the terms of the GNU General Public License as published by the Free Software 6 | Foundation, either version 3 of the License, or (at your option) any later 7 | version. 8 | This program is distributed in the hope that it will be useful, but WITHOUT 9 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 10 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 11 | You should have received a copy of the GNU General Public License along with 12 | this program. If not, see . 13 | 14 | __author__ = "bzhxx" 15 | __contact__ = "https://github.com/bzhxx" 16 | __license__ = "GPLv3" 17 | 18 | */ 19 | #ifndef _gwenesis_savestate_H_ 20 | #define _gwenesis_savestate_H_ 21 | 22 | #pragma once 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | typedef struct SaveState SaveState; 29 | 30 | bool initLoadGwenesisState(unsigned char *srcBuffer); 31 | int saveGwenesisState(unsigned char *destBuffer, int save_size); 32 | int loadGwenesisState(unsigned char *srcBuffer); 33 | 34 | SaveState* saveGwenesisStateOpenForRead(const char* fileName); 35 | SaveState* saveGwenesisStateOpenForWrite(const char* fileName); 36 | 37 | int saveGwenesisStateGet(SaveState* state, const char* tagName); 38 | void saveGwenesisStateSet(SaveState* state, const char* tagName, int value); 39 | 40 | void saveGwenesisStateGetBuffer(SaveState* state, const char* tagName, void* buffer, int length); 41 | void saveGwenesisStateSetBuffer(SaveState* state, const char* tagName, void* buffer, int length); 42 | 43 | void gwenesis_save_state(); 44 | void gwenesis_load_state(); 45 | #endif -------------------------------------------------------------------------------- /src/gwenesis/sound/gwenesis_sn76489.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _GWENESIS_SN76489_H_ 3 | #define _GWENESIS_SN76489_H_ 4 | 5 | /* 6 | More testing is needed to find and confirm feedback patterns for 7 | SN76489 variants and compatible chips. 8 | */ 9 | 10 | #undef uint8 11 | #undef uint16 12 | #undef uint32 13 | #undef uint64 14 | 15 | typedef unsigned char uint8; 16 | typedef unsigned short int uint16; 17 | typedef unsigned int uint32; 18 | 19 | typedef signed char int8; 20 | typedef signed short int int16; 21 | typedef signed int int32; 22 | 23 | typedef unsigned char UINT8; 24 | typedef unsigned short int UINT16; 25 | typedef unsigned int UINT32; 26 | 27 | typedef signed char INT8; 28 | typedef signed short int INT16; 29 | typedef signed int INT32; 30 | typedef long signed int INT641; 31 | 32 | typedef struct 33 | { 34 | /* Variables */ 35 | float Clock; 36 | float dClock; 37 | int NumClocksForSample; 38 | int WhiteNoiseFeedback; 39 | int divisor; 40 | 41 | /* PSG registers: */ 42 | UINT16 Registers[8]; /* Tone, vol x4 */ 43 | int LatchedRegister; 44 | UINT16 NoiseShiftRegister; 45 | INT16 NoiseFreq; /* Noise channel signal generator frequency */ 46 | 47 | /* Output calculation variables */ 48 | INT16 ToneFreqVals[4]; /* Frequency register values (counters) */ 49 | INT8 ToneFreqPos[4]; /* Frequency channel flip-flops */ 50 | INT16 Channels[4]; /* Value of each channel, before stereo is applied */ 51 | INT641 IntermediatePos[4]; /* intermediate values used at boundaries between + and - */ 52 | 53 | } SN76489_Context; 54 | 55 | extern int16 gwenesis_sn76489_buffer[]; 56 | extern int sn76489_index; 57 | extern int sn76489_clock; 58 | 59 | /* Function prototypes */ 60 | void gwenesis_SN76489_Init( int PSGClockValue, int SamplingRate,int freq_divisor); 61 | void gwenesis_SN76489_Reset(); 62 | void gwenesis_SN76489_start(); 63 | void gwenesis_SN76489_SetContext(uint8 *data); 64 | void gwenesis_SN76489_GetContext(uint8 *data); 65 | uint8 *gwenesis_SN76489_GetContextPtr(); 66 | int gwenesis_SN76489_GetContextSize(void); 67 | void gwenesis_SN76489_Write(int data, int target); 68 | void gwenesis_SN76489_run(int target); 69 | 70 | void gwenesis_sn76489_save_state(); 71 | void gwenesis_sn76489_load_state(); 72 | 73 | #endif /* _GWENESIS_SN76489_H_ */ 74 | -------------------------------------------------------------------------------- /src/gwenesis/sound/sin_tab.h: -------------------------------------------------------------------------------- 1 | const unsigned int __in_flash() __aligned(4) sin_tab[] = { 2 | 4274, 3462, 3086, 2838, 2652, 2504, 2380, 2274, 2182, 2100, 2026, 1958, 1898, 1840, 1788, 1738 // 0x00000000 3 | , 1692, 1650, 1608, 1570, 1534, 1498, 1464, 1434, 1402, 1374, 1344, 1318, 1292, 1266, 1242, 1218 // 0x00000040 4 | , 1196, 1174, 1152, 1132, 1112, 1092, 1072, 1054, 1036, 1018, 1002, 984, 968, 952, 936, 922 // 0x00000080 5 | , 906, 892, 878, 864, 850, 836, 822, 810, 798, 784, 772, 760, 750, 738, 726, 716 // 0x000000C0 6 | , 704, 694, 682, 672, 662, 652, 642, 632, 622, 614, 604, 594, 586, 578, 568, 560 // 0x00000100 7 | , 552, 542, 534, 526, 518, 510, 502, 496, 488, 480, 472, 466, 458, 452, 444, 438 // 0x00000140 8 | , 430, 424, 418, 410, 404, 398, 392, 386, 380, 374, 368, 362, 356, 350, 344, 338 // 0x00000180 9 | , 334, 328, 322, 318, 312, 306, 302, 296, 292, 286, 282, 276, 272, 268, 262, 258 // 0x000001C0 10 | , 254, 250, 244, 240, 236, 232, 228, 224, 220, 216, 212, 208, 204, 200, 196, 192 // 0x00000200 11 | , 188, 184, 182, 178, 174, 170, 166, 164, 160, 156, 154, 150, 148, 144, 140, 138 // 0x00000240 12 | , 134, 132, 128, 126, 124, 120, 118, 114, 112, 110, 106, 104, 102, 98, 96, 94 // 0x00000280 13 | , 92, 90, 86, 84, 82, 80, 78, 76, 74, 72, 70, 68, 66, 64, 62, 60 // 0x000002C0 14 | , 58, 56, 54, 52, 50, 48, 46, 46, 44, 42, 40, 40, 38, 36, 34, 34 // 0x00000300 15 | , 32, 30, 30, 28, 26, 26, 24, 24, 22, 20, 20, 18, 18, 16, 16, 14 // 0x00000340 16 | , 14, 14, 12, 12, 10, 10, 10, 8, 8, 8, 6, 6, 6, 4, 4, 4 // 0x00000380 17 | , 4, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0 // 0x000003C0 18 | , 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 4 // 0x00000400 19 | , 4, 4, 4, 6, 6, 6, 8, 8, 8, 10, 10, 10, 12, 12, 14, 14 // 0x00000440 20 | , 14, 16, 16, 18, 18, 20, 20, 22, 24, 24, 26, 26, 28, 30, 30, 32 // 0x00000480 21 | , 34, 34, 36, 38, 40, 40, 42, 44, 46, 46, 48, 50, 52, 54, 56, 58 // 0x000004C0 22 | , 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 90, 92 // 0x00000500 23 | , 94, 96, 98, 102, 104, 106, 110, 112, 114, 118, 120, 124, 126, 128, 132, 134 // 0x00000540 24 | , 138, 140, 144, 148, 150, 154, 156, 160, 164, 166, 170, 174, 178, 182, 184, 188 // 0x00000580 25 | , 192, 196, 200, 204, 208, 212, 216, 220, 224, 228, 232, 236, 240, 244, 250, 254 // 0x000005C0 26 | , 258, 262, 268, 272, 276, 282, 286, 292, 296, 302, 306, 312, 318, 322, 328, 334 // 0x00000600 27 | , 338, 344, 350, 356, 362, 368, 374, 380, 386, 392, 398, 404, 410, 418, 424, 430 // 0x00000640 28 | , 438, 444, 452, 458, 466, 472, 480, 488, 496, 502, 510, 518, 526, 534, 542, 552 // 0x00000680 29 | , 560, 568, 578, 586, 594, 604, 614, 622, 632, 642, 652, 662, 672, 682, 694, 704 // 0x000006C0 30 | , 716, 726, 738, 750, 760, 772, 784, 798, 810, 822, 836, 850, 864, 878, 892, 906 // 0x00000700 31 | , 922, 936, 952, 968, 984, 1002, 1018, 1036, 1054, 1072, 1092, 1112, 1132, 1152, 1174, 1196 // 0x00000740 32 | , 1218, 1242, 1266, 1292, 1318, 1344, 1374, 1402, 1434, 1464, 1498, 1534, 1570, 1608, 1650, 1692 // 0x00000780 33 | , 1738, 1788, 1840, 1898, 1958, 2026, 2100, 2182, 2274, 2380, 2504, 2652, 2838, 3086, 3462, 4274 // 0x000007C0 34 | , 4275, 3463, 3087, 2839, 2653, 2505, 2381, 2275, 2183, 2101, 2027, 1959, 1899, 1841, 1789, 1739 // 0x00000800 35 | , 1693, 1651, 1609, 1571, 1535, 1499, 1465, 1435, 1403, 1375, 1345, 1319, 1293, 1267, 1243, 1219 // 0x00000840 36 | , 1197, 1175, 1153, 1133, 1113, 1093, 1073, 1055, 1037, 1019, 1003, 985, 969, 953, 937, 923 // 0x00000880 37 | , 907, 893, 879, 865, 851, 837, 823, 811, 799, 785, 773, 761, 751, 739, 727, 717 // 0x000008C0 38 | , 705, 695, 683, 673, 663, 653, 643, 633, 623, 615, 605, 595, 587, 579, 569, 561 // 0x00000900 39 | , 553, 543, 535, 527, 519, 511, 503, 497, 489, 481, 473, 467, 459, 453, 445, 439 // 0x00000940 40 | , 431, 425, 419, 411, 405, 399, 393, 387, 381, 375, 369, 363, 357, 351, 345, 339 // 0x00000980 41 | , 335, 329, 323, 319, 313, 307, 303, 297, 293, 287, 283, 277, 273, 269, 263, 259 // 0x000009C0 42 | , 255, 251, 245, 241, 237, 233, 229, 225, 221, 217, 213, 209, 205, 201, 197, 193 // 0x00000A00 43 | , 189, 185, 183, 179, 175, 171, 167, 165, 161, 157, 155, 151, 149, 145, 141, 139 // 0x00000A40 44 | , 135, 133, 129, 127, 125, 121, 119, 115, 113, 111, 107, 105, 103, 99, 97, 95 // 0x00000A80 45 | , 93, 91, 87, 85, 83, 81, 79, 77, 75, 73, 71, 69, 67, 65, 63, 61 // 0x00000AC0 46 | , 59, 57, 55, 53, 51, 49, 47, 47, 45, 43, 41, 41, 39, 37, 35, 35 // 0x00000B00 47 | , 33, 31, 31, 29, 27, 27, 25, 25, 23, 21, 21, 19, 19, 17, 17, 15 // 0x00000B40 48 | , 15, 15, 13, 13, 11, 11, 11, 9, 9, 9, 7, 7, 7, 5, 5, 5 // 0x00000B80 49 | , 5, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1 // 0x00000BC0 50 | , 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 5 // 0x00000C00 51 | , 5, 5, 5, 7, 7, 7, 9, 9, 9, 11, 11, 11, 13, 13, 15, 15 // 0x00000C40 52 | , 15, 17, 17, 19, 19, 21, 21, 23, 25, 25, 27, 27, 29, 31, 31, 33 // 0x00000C80 53 | , 35, 35, 37, 39, 41, 41, 43, 45, 47, 47, 49, 51, 53, 55, 57, 59 // 0x00000CC0 54 | , 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 87, 91, 93 // 0x00000D00 55 | , 95, 97, 99, 103, 105, 107, 111, 113, 115, 119, 121, 125, 127, 129, 133, 135 // 0x00000D40 56 | , 139, 141, 145, 149, 151, 155, 157, 161, 165, 167, 171, 175, 179, 183, 185, 189 // 0x00000D80 57 | , 193, 197, 201, 205, 209, 213, 217, 221, 225, 229, 233, 237, 241, 245, 251, 255 // 0x00000DC0 58 | , 259, 263, 269, 273, 277, 283, 287, 293, 297, 303, 307, 313, 319, 323, 329, 335 // 0x00000E00 59 | , 339, 345, 351, 357, 363, 369, 375, 381, 387, 393, 399, 405, 411, 419, 425, 431 // 0x00000E40 60 | , 439, 445, 453, 459, 467, 473, 481, 489, 497, 503, 511, 519, 527, 535, 543, 553 // 0x00000E80 61 | , 561, 569, 579, 587, 595, 605, 615, 623, 633, 643, 653, 663, 673, 683, 695, 705 // 0x00000EC0 62 | , 717, 727, 739, 751, 761, 773, 785, 799, 811, 823, 837, 851, 865, 879, 893, 907 // 0x00000F00 63 | , 923, 937, 953, 969, 985, 1003, 1019, 1037, 1055, 1073, 1093, 1113, 1133, 1153, 1175, 1197 // 0x00000F40 64 | , 1219, 1243, 1267, 1293, 1319, 1345, 1375, 1403, 1435, 1465, 1499, 1535, 1571, 1609, 1651, 1693 // 0x00000F80 65 | , 1739, 1789, 1841, 1899, 1959, 2027, 2101, 2183, 2275, 2381, 2505, 2653, 2839, 3087, 3463, 4275}; 66 | -------------------------------------------------------------------------------- /src/gwenesis/sound/ym2612.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** 3 | ** software implementation of Yamaha FM sound generator (YM2612/YM3438) 4 | ** 5 | ** Original code (MAME fm.c) 6 | ** 7 | ** Copyright (C) 2001, 2002, 2003 Jarek Burczynski (bujar at mame dot net) 8 | ** Copyright (C) 1998 Tatsuyuki Satoh , MultiArcadeMachineEmulator development 9 | ** 10 | ** Version 1.4 (final beta) 11 | ** 12 | ** Additional code & fixes by Eke-Eke for Genesis Plus GX 13 | ** 14 | */ 15 | 16 | #ifndef _H_YM2612_ 17 | #define _H_YM2612_ 18 | 19 | extern int16_t gwenesis_sn76489_buffer[]; 20 | ///extern int ym2612_index; 21 | ///extern int ym2612_clock; 22 | 23 | extern int snd_output_volume; 24 | 25 | extern void YM2612Init(); 26 | extern void YM2612Config(unsigned char dac_bits); //,unsigned int AUDIO_FREQ_DIVISOR); 27 | extern void YM2612ResetChip(void); 28 | //extern void YM2612Update(int16_t *buffer, int length); 29 | extern void YM2612Write(unsigned int a, unsigned int v, int target); 30 | extern void ym2612_run(int target); 31 | extern unsigned int YM2612Read(int target); 32 | 33 | #if 0 34 | extern int YM2612LoadContext(unsigned char *state); 35 | extern int YM2612SaveContext(unsigned char *state); 36 | #endif 37 | 38 | //extern void YM2612LoadRegs(uint8_t *regs); 39 | //extern void YM2612SaveRegs(uint8_t *regs); 40 | 41 | void gwenesis_ym2612_save_state(); 42 | void gwenesis_ym2612_load_state(); 43 | 44 | #endif /* _YM2612_ */ 45 | -------------------------------------------------------------------------------- /src/gwenesis/sound/ym2612_OPN_ST_dt_tab.h: -------------------------------------------------------------------------------- 1 | const unsigned int __in_flash() __aligned(4) ym2612_OPN_ST_dt_tab[8][32] = { 2 | // freqbase: 6.000000 3 | { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 4 | { 0, 0, 0, 0, 384, 384, 384, 384, 384, 384, 384, 384, 768, 768, 768, 768, 768, 1152, 1152, 1152, 1536, 1536, 1536, 1920, 1920, 2304, 2304, 2688, 3072, 3072, 3072, 3072 }, 5 | { 384, 384, 384, 384, 768, 768, 768, 768, 768, 1152, 1152, 1152, 1536, 1536, 1536, 1920, 1920, 2304, 2304, 2688, 3072, 3072, 3456, 3840, 4224, 4608, 4992, 5376, 6144, 6144, 6144, 6144 }, 6 | { 768, 768, 768, 768, 768, 1152, 1152, 1152, 1536, 1536, 1536, 1920, 1920, 2304, 2304, 2688, 3072, 3072, 3456, 3840, 4224, 4608, 4992, 5376, 6144, 6528, 7296, 7680, 8448, 8448, 8448, 8448 }, 7 | { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 8 | { 0, 0, 0, 0, -384, -384, -384, -384, -384, -384, -384, -384, -768, -768, -768, -768, -768, -1152, -1152, -1152, -1536, -1536, -1536, -1920, -1920, -2304, -2304, -2688, -3072, -3072, -3072, -3072 }, 9 | { -384, -384, -384, -384, -768, -768, -768, -768, -768, -1152, -1152, -1152, -1536, -1536, -1536, -1920, -1920, -2304, -2304, -2688, -3072, -3072, -3456, -3840, -4224, -4608, -4992, -5376, -6144, -6144, -6144, -6144 }, 10 | { -768, -768, -768, -768, -768, -1152, -1152, -1152, -1536, -1536, -1536, -1920, -1920, -2304, -2304, -2688, -3072, -3072, -3456, -3840, -4224, -4608, -4992, -5376, -6144, -6528, -7296, -7680, -8448, -8448, -8448, -8448 } 11 | }; 12 | -------------------------------------------------------------------------------- /src/gwenesis/sound/z80inst.h: -------------------------------------------------------------------------------- 1 | /* 2 | Gwenesis : Genesis & megadrive Emulator. 3 | 4 | This program is free software: you can redistribute it and/or modify it under 5 | the terms of the GNU General Public License as published by the Free Software 6 | Foundation, either version 3 of the License, or (at your option) any later 7 | version. 8 | This program is distributed in the hope that it will be useful, but WITHOUT 9 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 10 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 11 | You should have received a copy of the GNU General Public License along with 12 | this program. If not, see . 13 | 14 | __author__ = "bzhxx" 15 | __contact__ = "https://github.com/bzhxx" 16 | __license__ = "GPLv3" 17 | 18 | */ 19 | #ifndef _Z80_INTERFACE_H_ 20 | #define _Z80_INTERFACE_H_ 21 | 22 | void z80_write_ctrl(unsigned int address, unsigned int value); 23 | unsigned int z80_read_ctrl(unsigned int address); 24 | void z80_start(); 25 | void z80_pulse_reset(); 26 | void z80_execute(unsigned int target); 27 | void z80_run(int target); 28 | extern int zclk; 29 | 30 | void gwenesis_z80inst_save_state(); 31 | void gwenesis_z80inst_load_state(); 32 | 33 | void z80_set_memory(unsigned char *buffer); 34 | 35 | void z80_write_memory_8(unsigned int address, unsigned int value); 36 | void z80_write_memory_16(unsigned int address, unsigned int value); 37 | unsigned int z80_read_memory_16(unsigned int address); 38 | unsigned int z80_read_memory_8(unsigned int address); 39 | void z80_irq_line(unsigned int value); 40 | 41 | void gwenesis_z80inst_save_state(); 42 | void gwenesis_z80inst_load_state(); 43 | 44 | #endif -------------------------------------------------------------------------------- /src/gwenesis/vdp/gwenesis_vdp.h: -------------------------------------------------------------------------------- 1 | /* 2 | Gwenesis : Genesis & megadrive Emulator. 3 | 4 | This program is free software: you can redistribute it and/or modify it under 5 | the terms of the GNU General Public License as published by the Free Software 6 | Foundation, either version 3 of the License, or (at your option) any later 7 | version. 8 | This program is distributed in the hope that it will be useful, but WITHOUT 9 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 10 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 11 | You should have received a copy of the GNU General Public License along with 12 | this program. If not, see . 13 | 14 | __author__ = "bzhxx" 15 | __contact__ = "https://github.com/bzhxx" 16 | __license__ = "GPLv3" 17 | 18 | */ 19 | #ifndef _gwenesis_vdp_H_ 20 | #define _gwenesis_vdp_H_ 21 | 22 | #pragma once 23 | 24 | #define BIT(v, idx) (((v) >> (idx)) & 1) 25 | #define BITS(v, idx, n) (((v) >> (idx)) & ((1 << (n)) - 1)) 26 | 27 | // VDP registers 28 | #define REG0_DISABLE_DISPLAY (gwenesis_vdp_regs[0] & 1) 29 | #define REG0_HVLATCH BIT(gwenesis_vdp_regs[0], 1) 30 | #define REG0_LINE_INTERRUPT BIT(gwenesis_vdp_regs[0], 4) 31 | #define REG1_PAL BIT(gwenesis_vdp_regs[1], 3) 32 | #define REG1_240_LINE ((gwenesis_vdp_regs[1] & 0x08) >> 3) 33 | #define REG1_DMA_ENABLED BIT(gwenesis_vdp_regs[1], 4) 34 | #define REG1_VBLANK_INTERRUPT BIT(gwenesis_vdp_regs[1], 5) 35 | #define REG1_DISP_ENABLED BIT(gwenesis_vdp_regs[1], 6) 36 | #define REG2_NAMETABLE_A (BITS(gwenesis_vdp_regs[2], 3, 3) << 13) 37 | #define REG3_NAMETABLE_W BITS(gwenesis_vdp_regs[3], 1, 5) 38 | #define REG4_NAMETABLE_B (BITS(gwenesis_vdp_regs[4], 0, 3) << 13) 39 | //#define REG5_SAT_ADDRESS ((gwenesis_vdp_regs[5] & (mode_h40 ? 0x7E : 0x7F)) << 9) 40 | //#define REG5_SAT_SIZE (mode_h40 ? (1 << 10) : (1 << 9)) 41 | #define REG5_SAT_ADDRESS ((gwenesis_vdp_regs[5] & ((gwenesis_vdp_regs[12] & 0x01) ? 0x7E : 0x7F)) << 9) 42 | #define REG5_SAT_SIZE ((gwenesis_vdp_regs[12] & 0x01) ? (1 << 10) : (1 << 9)) 43 | #define REG10_LINE_COUNTER BITS(gwenesis_vdp_regs[10], 0, 8) 44 | #define REG10_COLUMN_COUNTER BITS(gwenesis_vdp_regs[10], 8, 15) 45 | #define REG11_HSCROLL_MODE ((gwenesis_vdp_regs[11] & 3)) 46 | #define REG11_VSCROLL_MODE ((gwenesis_vdp_regs[11] & 4) >> 2) 47 | #define REG12_RS0 (gwenesis_vdp_regs[12] & 0x80) >> 7 48 | #define REG12_RS1 (gwenesis_vdp_regs[12] & 0x01) >> 0 49 | #define REG12_MODE_H40 (gwenesis_vdp_regs[12] & 1) 50 | #define REG13_HSCROLL_ADDRESS (gwenesis_vdp_regs[13] << 10) 51 | #define REG15_DMA_INCREMENT gwenesis_vdp_regs[15] 52 | #define REG16_UNUSED1 ((gwenesis_vdp_regs[16] & 0xc0) >> 6) 53 | #define REG16_VSCROLL_SIZE ((gwenesis_vdp_regs[16] >> 4) & 3) 54 | #define REG16_UNUSED2 ((gwenesis_vdp_regs[16] & 0x0c) >> 2) 55 | #define REG16_HSCROLL_SIZE (gwenesis_vdp_regs[16] & 3) 56 | #define REG17_WINDOW_HPOS BITS(gwenesis_vdp_regs[17], 0, 5) 57 | #define REG17_WINDOW_RIGHT ((gwenesis_vdp_regs[17] & 0x80) >> 7) 58 | #define REG18_WINDOW_DOWN ((gwenesis_vdp_regs[0x12] & 0x80) >> 7) 59 | #define REG18_WINDOW_VPOS BITS(gwenesis_vdp_regs[18], 0, 5) 60 | #define REG19_DMA_LENGTH (gwenesis_vdp_regs[19] | (gwenesis_vdp_regs[20] << 8)) 61 | #define REG21_DMA_SRCADDR_LOW (gwenesis_vdp_regs[21] | (gwenesis_vdp_regs[22] << 8)) 62 | #define REG23_DMA_SRCADDR_HIGH ((gwenesis_vdp_regs[23] & 0x7F) << 16) 63 | #define REG23_DMA_TYPE BITS(gwenesis_vdp_regs[23], 6, 2) 64 | 65 | //VDP status register 66 | #define STATUS_FIFO_EMPTY (1 << 9) 67 | #define STATUS_FIFO_FULL (1 << 8) 68 | #define STATUS_VIRQPENDING (1 << 7) 69 | #define STATUS_SPRITEOVERFLOW (1 << 6) 70 | #define STATUS_SPRITECOLLISION (1 << 5) 71 | #define STATUS_ODDFRAME (1 << 4) 72 | #define STATUS_VBLANK (1 << 3) 73 | #define STATUS_HBLANK (1 << 2) 74 | #define STATUS_DMAPROGRESS (1 << 1) 75 | #define STATUS_PAL (1 << 0) 76 | 77 | #define VRAM_MAX_SIZE 0x10000 // VRAM maximum size 78 | #define CRAM_MAX_SIZE 0x40 // CRAM maximum size 79 | #define VSRAM_MAX_SIZE 0x40 // VSRAM maximum size 80 | #define SAT_CACHE_MAX_SIZE 0x400 // SAT CACHE maximum size 81 | #define REG_SIZE 0x20 // REGISTERS total 82 | #define FIFO_SIZE 0x4 // FIFO maximum size 83 | 84 | #define COLOR_3B_TO_8B(c) (((c) << 5) | ((c) << 2) | ((c) >> 1)) 85 | #define CRAM_R(c) COLOR_3B_TO_8B(BITS((c), 1, 3)) 86 | #define CRAM_G(c) COLOR_3B_TO_8B(BITS((c), 5, 3)) 87 | #define CRAM_B(c) COLOR_3B_TO_8B(BITS((c), 9, 3)) 88 | 89 | #define MODE_SHI BITS(gwenesis_vdp_regs[12], 3, 1) 90 | 91 | #define SHADOW_COLOR(r,g,b) \ 92 | do { r >>= 1; g >>= 1; b >>= 1; } while (0) 93 | #define HIGHLIGHT_COLOR(r,g,b) \ 94 | do { SHADOW_COLOR(r,g,b); r |= 0x80; g |= 0x80; b |= 0x80; } while(0) 95 | 96 | // While we draw the planes, we use bit 0x80 on each pixel to save the 97 | // high-priority flag, so that we can later prioritize. 98 | #define PIXATTR_HIPRI 0x80 99 | #define PIXATTR_LOWPRI 0x00 100 | #define PIXATTR_SPRITE 0x40 101 | #define PIXATTR_SPRITE_HIPRI 0xC0 102 | 103 | 104 | // After mixing code, we use free bits 0x80 and 0x40 to indicate the 105 | // shadow/highlight effect to apply on each pixel. Notice that we use 106 | // 0x80 to indicate normal drawing and 0x00 to indicate shadowing, 107 | // which does match exactly the semantic of PIXATTR_HIPRI. This simplifies 108 | // mixing code quite a bit. 109 | #define SHI_NORMAL(x) ((x) | 0x80) 110 | #define SHI_HIGHLIGHT(x) ((x) | 0x40) 111 | #define SHI_SHADOW(x) ((x) & 0x3F) 112 | 113 | #define SHI_IS_SHADOW(x) (!((x) & 0x80)) 114 | #define SHI_IS_HIGHLIGHT(x) ((x) & 0x40) 115 | 116 | void gwenesis_vdp_reset(); 117 | void gwenesis_vdp_set_hblank(); 118 | void gwenesis_vdp_clear_hblank(); 119 | void gwenesis_vdp_set_vblank(); 120 | void gwenesis_vdp_clear_vblank(); 121 | 122 | unsigned int gwenesis_vdp_get_reg(int reg); 123 | void gwenesis_vdp_set_reg(int reg, unsigned char value); 124 | 125 | unsigned int gwenesis_vdp_read_memory_8(unsigned int address); 126 | unsigned int gwenesis_vdp_read_memory_16(unsigned int address); 127 | 128 | void gwenesis_vdp_write_memory_8(unsigned int address, unsigned int value); 129 | void gwenesis_vdp_write_memory_16(unsigned int address, unsigned int value); 130 | 131 | void gwenesis_vdp_set_buffers(unsigned char *screen_buffer, unsigned char *scaled_buffer); 132 | void gwenesis_vdp_set_buffer(uint8_t *ptr_screen_buffer); 133 | void gwenesis_vdp_get_buffer(uint16_t** ptr_screen_buffer); 134 | void gwenesis_vdp_render_line(int line); 135 | 136 | void gwenesis_vdp_render_config(); 137 | 138 | unsigned int gwenesis_vdp_get_status(); 139 | void gwenesis_vdp_get_debug_status(char *s); 140 | unsigned short gwenesis_vdp_get_cram(int index); 141 | void gwenesis_vdp_get_vram(unsigned char *raw_buffer, int palette); 142 | void gwenesis_vdp_get_vram_raw(unsigned char *raw_buffer); 143 | void gwenesis_vdp_get_cram_raw(unsigned char *raw_buffer); 144 | 145 | int gwenesis_vdp_vcounter(); 146 | int gwenesis_vdp_hcounter(); 147 | unsigned short gwenesis_vdp_hvcounter(); 148 | 149 | void gwenesis_vdp_gfx_save_state(); 150 | void gwenesis_vdp_gfx_load_state(); 151 | void gwenesis_vdp_mem_save_state(); 152 | void gwenesis_vdp_mem_load_state(); 153 | 154 | #endif -------------------------------------------------------------------------------- /src/tusb_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 Ha Thach (tinyusb.org) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #ifndef _TUSB_CONFIG_H_ 27 | #define _TUSB_CONFIG_H_ 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | //--------------------------------------------------------------------+ 34 | // Board Specific Configuration 35 | //--------------------------------------------------------------------+ 36 | 37 | #if CFG_TUSB_MCU == OPT_MCU_RP2040 38 | // change to 1 if using pico-pio-usb as host controller for raspberry rp2040 39 | #define CFG_TUH_RPI_PIO_USB 0 40 | #define BOARD_TUH_RHPORT CFG_TUH_RPI_PIO_USB 41 | #endif 42 | 43 | // RHPort number used for host can be defined by board.mk, default to port 0 44 | #ifndef BOARD_TUH_RHPORT 45 | #define BOARD_TUH_RHPORT 0 46 | #endif 47 | 48 | // RHPort max operational speed can defined by board.mk 49 | #ifndef BOARD_TUH_MAX_SPEED 50 | #define BOARD_TUH_MAX_SPEED OPT_MODE_DEFAULT_SPEED 51 | #endif 52 | 53 | //-------------------------------------------------------------------- 54 | // COMMON CONFIGURATION 55 | //-------------------------------------------------------------------- 56 | 57 | // defined by compiler flags for flexibility 58 | #ifndef CFG_TUSB_MCU 59 | #error CFG_TUSB_MCU must be defined 60 | #endif 61 | 62 | #ifndef CFG_TUSB_OS 63 | #define CFG_TUSB_OS OPT_OS_NONE 64 | #endif 65 | 66 | #ifndef CFG_TUSB_DEBUG 67 | #define CFG_TUSB_DEBUG 0 68 | #endif 69 | 70 | // Enable Host stack 71 | #define CFG_TUH_ENABLED 1 72 | 73 | // Default is max speed that hardware controller could support with on-chip PHY 74 | #define CFG_TUH_MAX_SPEED BOARD_TUH_MAX_SPEED 75 | 76 | /* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. 77 | * Tinyusb use follows macros to declare transferring memory so that they can be put 78 | * into those specific section. 79 | * e.g 80 | * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) 81 | * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) 82 | */ 83 | #ifndef CFG_TUSB_MEM_SECTION 84 | #define CFG_TUSB_MEM_SECTION 85 | #endif 86 | 87 | #ifndef CFG_TUSB_MEM_ALIGN 88 | #define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) 89 | #endif 90 | 91 | //-------------------------------------------------------------------- 92 | // CONFIGURATION 93 | //-------------------------------------------------------------------- 94 | 95 | // Size of buffer to hold descriptors and other data used for enumeration 96 | #define CFG_TUH_ENUMERATION_BUFSIZE 1024 97 | 98 | #define CFG_TUH_XINPUT 1 // 99 | #define CFG_TUH_HUB 0 // number of supported hubs 100 | #define CFG_TUH_CDC 0 101 | #define CFG_TUH_HID 4 // typical keyboard + mouse device can have 3-4 HID interfaces 102 | #define CFG_TUH_MSC 0 103 | #define CFG_TUH_VENDOR 0 104 | 105 | // max device support (excluding hub device) 106 | #define CFG_TUH_DEVICE_MAX (CFG_TUH_HUB ? 4 : 1) // hub typically has 4 ports 107 | 108 | //------------- HID -------------// 109 | #define CFG_TUH_HID_EPIN_BUFSIZE 64 110 | #define CFG_TUH_HID_EPOUT_BUFSIZE 64 111 | 112 | //------------- CDC -------------// 113 | 114 | // Set Line Control state on enumeration/mounted: 115 | // DTR ( bit 0), RTS (bit 1) 116 | #define CFG_TUH_CDC_LINE_CONTROL_ON_ENUM 0x03 117 | 118 | // Set Line Coding on enumeration/mounted, value for cdc_line_coding_t 119 | // bit rate = 115200, 1 stop bit, no parity, 8 bit data width 120 | #define CFG_TUH_CDC_LINE_CODING_ON_ENUM { 115200, CDC_LINE_CONDING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 } 121 | 122 | 123 | #ifdef __cplusplus 124 | } 125 | #endif 126 | 127 | #endif /* _TUSB_CONFIG_H_ */ 128 | --------------------------------------------------------------------------------