├── .gitignore ├── img ├── cover.jpg ├── ss01.png ├── circuit.png ├── backside.jpg └── bootmenu.jpg ├── samples ├── font │ └── mono8x16 │ │ ├── font2cpp.sh │ │ ├── mono8x16.png │ │ ├── mono8x16.hpp │ │ ├── font2cpp.py │ │ ├── mono8x16.cpp │ │ └── mono8x16_bmp.cpp ├── pico_ws19804 │ ├── boot_menu.hpp │ ├── ws19804.pio │ ├── ws19804_c.h │ ├── Makefile.sample.mk │ ├── common.hpp │ ├── common.cpp │ ├── ws19804.hpp │ ├── CMakeLists.txt │ ├── pwm_audio.hpp │ ├── boot_menu.cpp │ ├── pico_nes_ws19804.cpp │ └── ws19804.cpp ├── wxapp │ ├── nes_audio.hpp │ ├── nes_screen.hpp │ ├── Makefile.sample.mk │ ├── nes_audio.cpp │ ├── nes_app.cpp │ └── nes_screen.cpp └── fatfs │ ├── source │ ├── 00readme.txt │ ├── diskio.h │ ├── diskio.c │ ├── ffsystem.c │ ├── ffconf.h │ ├── 00history.txt │ ├── ff.h │ └── mmc_pico_spi.c │ └── LICENSE.txt ├── core ├── include │ └── shapones │ │ ├── utils_std.hpp │ │ ├── dma.hpp │ │ ├── common.hpp │ │ ├── interrupt.hpp │ │ ├── input.hpp │ │ ├── shapones.hpp │ │ ├── memory.hpp │ │ ├── cpu.hpp │ │ ├── apu.hpp │ │ └── ppu.hpp └── src │ ├── interrupt.cpp │ ├── shapones.cpp │ ├── utils_std.cpp │ ├── dma.cpp │ ├── input.cpp │ ├── memory.cpp │ ├── apu.cpp │ └── ppu.cpp ├── roms ├── README.md └── nes2cpp.py └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | build/ 3 | bin/ 4 | *.nes 5 | nes_rom.cpp 6 | -------------------------------------------------------------------------------- /img/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shapoco/shapones/HEAD/img/cover.jpg -------------------------------------------------------------------------------- /img/ss01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shapoco/shapones/HEAD/img/ss01.png -------------------------------------------------------------------------------- /img/circuit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shapoco/shapones/HEAD/img/circuit.png -------------------------------------------------------------------------------- /img/backside.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shapoco/shapones/HEAD/img/backside.jpg -------------------------------------------------------------------------------- /img/bootmenu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shapoco/shapones/HEAD/img/bootmenu.jpg -------------------------------------------------------------------------------- /samples/font/mono8x16/font2cpp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./font2cpp.py mono8x16.png mono8x16 > mono8x16_bmp.cpp 4 | -------------------------------------------------------------------------------- /samples/font/mono8x16/mono8x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shapoco/shapones/HEAD/samples/font/mono8x16/mono8x16.png -------------------------------------------------------------------------------- /samples/pico_ws19804/boot_menu.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BOOT_MENU_HPP 2 | #define BOOT_MENU_HPP 3 | 4 | // show boot menu 5 | bool boot_menu(); 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /samples/pico_ws19804/ws19804.pio: -------------------------------------------------------------------------------- 1 | .program ws19804 2 | .side_set 1 3 | 4 | .wrap_target 5 | out pins, 1 side 0 6 | in pins, 1 side 1 7 | .wrap 8 | -------------------------------------------------------------------------------- /core/include/shapones/utils_std.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FC_UTILS_STD 2 | #define FC_UTILS_STD 3 | 4 | namespace nes { 5 | 6 | void load_ines_file(const char* path); 7 | 8 | } 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /core/include/shapones/dma.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SHAPONES_DMA_HPP 2 | #define SHAPONES_DMA_HPP 3 | 4 | #include "shapones/shapones.hpp" 5 | 6 | namespace nes::dma { 7 | 8 | static constexpr int TRANSFER_SIZE = 256; 9 | 10 | bool is_running(); 11 | void start(int src_page); 12 | int exec_next_cycle(); 13 | 14 | } 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /roms/README.md: -------------------------------------------------------------------------------- 1 | - helloworld.nes, SHOOT.nes 2 | - http://hp.vector.co.jp/authors/VA042397/nes/sample.html 3 | - nestest.nes 4 | - https://www.nesdev.org/wiki/Emulator_tests 5 | - nim_nom_v1.2.nes 6 | - https://forums.nesdev.org/viewtopic.php?p=215467 7 | - falling.nes 8 | - https://github.com/xram64/falling-nes 9 | -------------------------------------------------------------------------------- /core/include/shapones/common.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SHAPONES_BASIC_HPP 2 | #define SHAPONES_BASIC_HPP 3 | 4 | #include "stdint.h" 5 | 6 | namespace nes { 7 | 8 | using addr_t = uint16_t; 9 | using cycle_t = unsigned int; 10 | 11 | static constexpr int SCREEN_WIDTH = 256; 12 | static constexpr int SCREEN_HEIGHT = 240; 13 | 14 | } 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /samples/pico_ws19804/ws19804_c.h: -------------------------------------------------------------------------------- 1 | #ifndef WS19804_C_H 2 | #define WS19804_C_H 3 | 4 | #include "stdint.h" 5 | 6 | // SPI write function for FatFS 7 | void ws19804_write_blocking(const uint8_t *data, int len); 8 | 9 | // SPI read function for FatFS 10 | void ws19804_read_blocking(uint8_t tx_repeat, uint8_t *buff, int len); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /core/include/shapones/interrupt.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SHAPONES_INTERRUPT_HPP 2 | #define SHAPONES_INTERRUPT_HPP 3 | 4 | #include "shapones.hpp" 5 | 6 | namespace nes::interrupt { 7 | 8 | void assert_irq(); 9 | void deassert_irq(); 10 | bool is_irq_asserted(); 11 | 12 | void assert_nmi(); 13 | void deassert_nmi(); 14 | bool is_nmi_asserted(); 15 | 16 | } 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ShapoNES 2 | 3 | an NES emulator for my programming study. 4 | 5 | ## Raspberry Pi Pico 6 | 7 | a sample using WAVESHARE-19804, supporting NES loading from SD card. 8 | 9 | ![](img/bootmenu.jpg) 10 | ![](img/cover.jpg) 11 | ![](img/backside.jpg) 12 | ![](img/circuit.png) 13 | 14 | ## PC (wxWidgets) 15 | 16 | a sample for PCs using wxWidgets. 17 | 18 | ![](img/ss01.png) 19 | -------------------------------------------------------------------------------- /core/src/interrupt.cpp: -------------------------------------------------------------------------------- 1 | #include "shapones/interrupt.hpp" 2 | 3 | namespace nes::interrupt { 4 | 5 | static bool irq = false; 6 | static bool nmi = false; 7 | 8 | void assert_irq() { irq = true; } 9 | void deassert_irq() { irq = false; } 10 | bool is_irq_asserted() { return irq; } 11 | 12 | void assert_nmi() { nmi = true; } 13 | void deassert_nmi() { nmi = false; } 14 | bool is_nmi_asserted() { return nmi; } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /samples/wxapp/nes_audio.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NES_AUDIO_HPP 2 | #define NES_AUDIO_HPP 3 | 4 | #include "stdint.h" 5 | #include 6 | #include 7 | #include 8 | 9 | namespace nes_audio { 10 | 11 | static constexpr int FREQ_HZ = 22050; 12 | static constexpr int LATENCY_US = 100 * 1000; 13 | static constexpr int CHUNK_SIZE = 256; 14 | 15 | void play(); 16 | void stop(); 17 | 18 | } 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /roms/nes2cpp.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python3 2 | 3 | import sys 4 | 5 | with open(sys.argv[1], 'rb') as f: 6 | data = f.read() 7 | 8 | print('#include "stdint.h"') 9 | print('#include "nes_rom.hpp"') 10 | print('const uint8_t nes_rom[] = {') 11 | 12 | n = len(data) 13 | for i in range(n): 14 | if (i % 16 == 0): 15 | print(' ', end='') 16 | print('0x%02x, ' % data[i], end='') 17 | if (i % 16 == 15) or (i == n - 1): 18 | print() 19 | 20 | print('}') 21 | -------------------------------------------------------------------------------- /samples/wxapp/nes_screen.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FC_SCREEN_HPP 2 | #define FC_SCREEN_HPP 3 | 4 | #include 5 | 6 | class FcScreen : public wxScrolledWindow { 7 | public: 8 | FcScreen(wxFrame *parent, wxWindowID id); 9 | void OnPaint(wxPaintEvent &event); 10 | void OnKeyDown(wxKeyEvent &event); 11 | void OnKeyUp(wxKeyEvent &event); 12 | void Render(); 13 | 14 | private: 15 | wxFrame *owner; 16 | wxImage frame_buff; 17 | DECLARE_EVENT_TABLE(); 18 | }; 19 | 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /core/src/shapones.cpp: -------------------------------------------------------------------------------- 1 | #include "shapones/shapones.hpp" 2 | 3 | namespace nes { 4 | 5 | void reset() { 6 | cpu::reset(); 7 | ppu::reset(); 8 | apu::reset(); 9 | } 10 | 11 | void render_next_line(uint8_t *line_buff) { 12 | bool eol; 13 | do { 14 | cpu::service(); 15 | eol = ppu::service(line_buff); 16 | } while (!eol); 17 | } 18 | 19 | void vsync(uint8_t *line_buff) { 20 | while (ppu::current_focus_y() != ppu::SCAN_LINES - 1) { 21 | render_next_line(line_buff); 22 | } 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /samples/font/mono8x16/mono8x16.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MONO8X16_HPP 2 | #define MONO8X16_HPP 3 | 4 | #include "stdint.h" 5 | 6 | namespace mono8x16 { 7 | 8 | extern const uint8_t bmp[]; 9 | 10 | void draw_char_rgb444( 11 | uint8_t *dest, 12 | int dest_stride, 13 | int dest_w, 14 | int dest_h, 15 | int x0, 16 | int y0, 17 | const char c, 18 | uint16_t color 19 | ); 20 | 21 | void draw_string_rgb444( 22 | uint8_t *dest, 23 | int dest_stride, 24 | int dest_w, 25 | int dest_h, 26 | int x0, 27 | int y0, 28 | const char* str, 29 | uint16_t color 30 | ); 31 | 32 | }; 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /core/src/utils_std.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "shapones/cpu.hpp" 7 | #include "shapones/utils_std.hpp" 8 | 9 | namespace nes { 10 | 11 | std::vector ines_image; 12 | 13 | void load_ines_file(const char* path) { 14 | std::ifstream ifs(path, std::ios::binary); 15 | 16 | ifs.seekg(0, std::ios::end); 17 | auto size = ifs.tellg(); 18 | ifs.seekg(0); 19 | 20 | std::vector vec(size); 21 | ifs.read((char*)&vec[0], size); 22 | ines_image = std::move(vec); 23 | 24 | memory::map_ines(&ines_image[0]); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /core/src/dma.cpp: -------------------------------------------------------------------------------- 1 | #include "shapones/shapones.hpp" 2 | 3 | namespace nes::dma { 4 | 5 | static bool running = false; 6 | static int next_cycle = 0; 7 | static addr_t next_src_addr = 0; 8 | 9 | bool is_running() { 10 | return running; 11 | } 12 | 13 | void start(int src_page) { 14 | running = true; 15 | next_cycle = 0; 16 | next_src_addr = (addr_t)src_page * 0x100; 17 | } 18 | 19 | int exec_next_cycle() { 20 | if ( ! running) return 0; 21 | 22 | ppu::oam_dma_write(next_cycle++, cpu::bus_read(next_src_addr++)); 23 | 24 | if (next_cycle >= TRANSFER_SIZE) { 25 | running = false; 26 | } 27 | return 2; 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /samples/pico_ws19804/Makefile.sample.mk: -------------------------------------------------------------------------------- 1 | .PHONY: all install clean 2 | 3 | REPO_DIR=$(shell pwd) 4 | SRC_DIR=. 5 | BUILD_DIR=build 6 | 7 | BIN_NAME=pico_nes_ws19804.uf2 8 | BIN=$(BUILD_DIR)/$(BIN_NAME) 9 | 10 | CORE_DIR=../../core 11 | FATFS_DIR=../fatfs/source 12 | 13 | SRC_LIST=\ 14 | $(wildcard $(SRC_DIR)/*.*) \ 15 | $(wildcard $(SRC_DIR)/../font/mono8x16/*.*) \ 16 | $(wildcard $(CORE_DIR)/src/*.*) \ 17 | $(wildcard $(CORE_DIR)/include/shapones/*.*) \ 18 | $(wildcard $(FATFS_DIR)/*.*) 19 | 20 | all: $(BIN) 21 | 22 | $(BIN): $(SRC_LIST) CMakeLists.txt 23 | mkdir -p $(BUILD_DIR) 24 | cd $(BUILD_DIR) \ 25 | && cmake .. \ 26 | && make -j 27 | @echo "------------------------------" 28 | @echo "UF2 File:" 29 | @echo $(REPO_DIR)/$(BIN) 30 | @ls -l $(REPO_DIR)/$(BIN) 31 | 32 | install: $(BIN) 33 | sudo mkdir -p /mnt/e 34 | sudo mount -t drvfs e: /mnt/e 35 | cp $(BIN) /mnt/e/. 36 | 37 | clean: 38 | rm -rf build 39 | -------------------------------------------------------------------------------- /samples/fatfs/source/00readme.txt: -------------------------------------------------------------------------------- 1 | FatFs Module Source Files R0.15 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 | -------------------------------------------------------------------------------- /core/src/input.cpp: -------------------------------------------------------------------------------- 1 | #include "shapones/shapones.hpp" 2 | 3 | namespace nes::input { 4 | 5 | static InputControl reg; 6 | static InputStatus raw[2]; 7 | static uint8_t shift_reg[2]; 8 | 9 | InputStatus get_raw(int player) { 10 | if (player < 0) player = 0; 11 | else if (player > 1) player = 1; 12 | return raw[player]; 13 | } 14 | 15 | void set_raw(int player, InputStatus s) { 16 | if (player < 0) player = 0; 17 | else if (player > 1) player = 1; 18 | raw[player] = s; 19 | } 20 | 21 | void update() { 22 | if (reg.strobe) { 23 | shift_reg[0] = raw[0].raw; 24 | shift_reg[1] = raw[1].raw; 25 | } 26 | } 27 | 28 | uint8_t read_latched(int player) { 29 | if (player < 0) player = 0; 30 | else if (player > 1) player = 1; 31 | uint8_t retval = shift_reg[player] & 1; 32 | shift_reg[player] >>= 1; 33 | return retval; 34 | } 35 | 36 | void write_control(uint8_t data) { 37 | reg.raw = data; 38 | update(); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /samples/wxapp/Makefile.sample.mk: -------------------------------------------------------------------------------- 1 | .PHONY: all install clean 2 | 3 | WXWIDGETS_PATH := $(shell echo $$HOME)/local/wxWidgets-3.2.1 4 | 5 | export PATH = $(shell echo $$PATH):$(WXWIDGETS_PATH)/bin 6 | export LIBRARY_PATH = $(shell echo $$LIBRARY_PATH):$(WXWIDGETS_PATH)/lib 7 | export LD_LIBRARY_PATH = $(shell echo $$LD_LIBRARY_PATH):$(WXWIDGETS_PATH)/lib 8 | 9 | SRC_DIR = . 10 | BIN_DIR = build 11 | ROM := ../../roms/nestest.nes 12 | 13 | CORE_DIR=../../core 14 | 15 | BIN_NAME = shapones 16 | BIN = $(BIN_DIR)/$(BIN_NAME) 17 | 18 | SRC_LIST = \ 19 | $(wildcard $(SRC_DIR)/*.*) \ 20 | $(wildcard $(CORE_DIR)/src/*.*) \ 21 | $(wildcard $(CORE_DIR)/include/shapones/*.*) 22 | 23 | all: $(BIN) 24 | 25 | $(BIN): $(SRC_LIST) 26 | mkdir -p $(BIN_DIR) 27 | g++ \ 28 | $(CORE_DIR)/src/*.cpp \ 29 | *.cpp \ 30 | -O4 \ 31 | -I$(CORE_DIR)/include \ 32 | -I. \ 33 | `wx-config --cflags` \ 34 | `wx-config --libs` \ 35 | -lpulse -lpulse-simple \ 36 | -o $(BIN) 37 | 38 | run: $(BIN) 39 | ./$(BIN) $(ROM) 40 | 41 | clean: 42 | rm -rf $(BIN_DIR) 43 | -------------------------------------------------------------------------------- /core/include/shapones/input.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SHAPONES_INPUT_HPP 2 | #define SHAPONES_INPUT_HPP 3 | 4 | #include "shapones/shapones.hpp" 5 | 6 | namespace nes::input { 7 | 8 | static constexpr int BTN_A = 0; 9 | static constexpr int BTN_B = 1; 10 | static constexpr int BTN_SELECT = 2; 11 | static constexpr int BTN_START = 3; 12 | static constexpr int BTN_UP = 4; 13 | static constexpr int BTN_DOWN = 5; 14 | static constexpr int BTN_LEFT = 6; 15 | static constexpr int BTN_RIGHT = 7; 16 | 17 | union InputStatus { 18 | uint8_t raw; 19 | struct { 20 | uint8_t A : 1; 21 | uint8_t B : 1; 22 | uint8_t select : 1; 23 | uint8_t start : 1; 24 | uint8_t up : 1; 25 | uint8_t down : 1; 26 | uint8_t left : 1; 27 | uint8_t right : 1; 28 | }; 29 | }; 30 | 31 | union InputControl { 32 | uint8_t raw; 33 | struct { 34 | uint8_t strobe : 1; 35 | uint8_t reserved : 7; 36 | }; 37 | }; 38 | 39 | InputStatus get_raw(int player); 40 | void set_raw(int player, InputStatus data); 41 | void update(); 42 | uint8_t read_latched(int player); 43 | void write_control(uint8_t data); 44 | 45 | } 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /core/include/shapones/shapones.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SHAPONES_HPP 2 | #define SHAPONES_HPP 3 | 4 | #include "stdio.h" 5 | #include "stdlib.h" 6 | 7 | //#include "pico/stdlib.h" 8 | //#include "hardware/gpio.h" 9 | 10 | #define NES_ALWAYS_INLINE __attribute__((always_inline)) static inline 11 | 12 | #if 1 13 | 14 | #define NES_PRINTF(fmt, ...) \ 15 | do { \ 16 | printf((fmt), ##__VA_ARGS__); \ 17 | fflush(stdout); \ 18 | } while(0) 19 | 20 | #define NES_ERRORF(fmt, ...) \ 21 | do { \ 22 | printf("*ERROR: "); \ 23 | NES_PRINTF(fmt, ##__VA_ARGS__); \ 24 | nes::cpu::stop(); \ 25 | } while(0) 26 | 27 | #else 28 | 29 | #define NES_PRINTF(fmt, ...) \ 30 | do { } while(0) 31 | 32 | #define NES_ERRORF(fmt, ...) \ 33 | do { \ 34 | nes::cpu::stop(); \ 35 | } while(0) 36 | 37 | #endif 38 | 39 | #include "shapones/common.hpp" 40 | #include "shapones/input.hpp" 41 | #include "shapones/interrupt.hpp" 42 | #include "shapones/memory.hpp" 43 | #include "shapones/cpu.hpp" 44 | #include "shapones/ppu.hpp" 45 | #include "shapones/dma.hpp" 46 | #include "shapones/apu.hpp" 47 | 48 | namespace nes { 49 | 50 | void reset(); 51 | void render_next_line(uint8_t *line_buff); 52 | void vsync(uint8_t *line_buff); 53 | 54 | } 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /samples/pico_ws19804/common.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_HPP 2 | #define COMMON_HPP 3 | 4 | #include "stdint.h" 5 | #include "pico/stdlib.h" 6 | #include "hardware/clocks.h" 7 | #include "hardware/gpio.h" 8 | #include "shapones/shapones.hpp" 9 | #include "ws19804.hpp" 10 | #include "mono8x16.hpp" 11 | 12 | // system clock frequency 13 | static constexpr uint32_t SYS_CLK_FREQ = 250 * MHZ; 14 | 15 | // frame buffer for DMA (RGB444) 16 | constexpr int FRAME_BUFF_WIDTH = 240; 17 | constexpr int FRAME_BUFF_STRIDE = FRAME_BUFF_WIDTH * 3 / 2; 18 | constexpr int FRAME_BUFF_HEIGHT = nes::SCREEN_HEIGHT; 19 | extern uint8_t frame_buff[FRAME_BUFF_STRIDE * nes::SCREEN_HEIGHT]; 20 | 21 | // pad pins 22 | static constexpr int PIN_PAD_A = 2; 23 | static constexpr int PIN_PAD_B = 3; 24 | static constexpr int PIN_PAD_START = 4; 25 | static constexpr int PIN_PAD_SELECT = 6; 26 | static constexpr int PIN_PAD_RIGHT = 7; 27 | static constexpr int PIN_PAD_DOWN = 14; 28 | static constexpr int PIN_PAD_LEFT = 26; 29 | static constexpr int PIN_PAD_UP = 27; 30 | extern const int input_pins[]; 31 | 32 | // wait until some key is pressed 33 | int wait_key(); 34 | 35 | // clear frame buffer with black color 36 | void clear_frame_buff(); 37 | 38 | // draw string to frame buffer 39 | void draw_string(int x, int y, const char *str); 40 | 41 | // transfer image from frame buffer to LCD 42 | void update_lcd(); 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /samples/pico_ws19804/common.cpp: -------------------------------------------------------------------------------- 1 | #include "common.hpp" 2 | 3 | uint8_t frame_buff[FRAME_BUFF_STRIDE * nes::SCREEN_HEIGHT]; 4 | 5 | const int input_pins[] = { 6 | PIN_PAD_A, PIN_PAD_B, PIN_PAD_SELECT, PIN_PAD_START, 7 | PIN_PAD_UP, PIN_PAD_DOWN, PIN_PAD_LEFT, PIN_PAD_RIGHT 8 | }; 9 | 10 | int wait_key() { 11 | // wait all button released 12 | int num_pushed; 13 | do { 14 | sleep_ms(10); 15 | num_pushed = 0; 16 | for (int i = 0; i < 8; i++) { 17 | if ( ! gpio_get(input_pins[i])) { 18 | num_pushed++; 19 | } 20 | } 21 | } while(num_pushed != 0); 22 | 23 | // wait any button pushed 24 | for(;;) { 25 | sleep_ms(10); 26 | for (int i = 0; i < 8; i++) { 27 | if ( ! gpio_get(input_pins[i])) { 28 | return i; 29 | } 30 | } 31 | } 32 | } 33 | 34 | void draw_string(int x, int y, const char *str) { 35 | mono8x16::draw_string_rgb444( 36 | frame_buff, FRAME_BUFF_STRIDE, FRAME_BUFF_WIDTH, FRAME_BUFF_HEIGHT, 37 | x, y, str, 0xfff); 38 | } 39 | 40 | void clear_frame_buff() { 41 | for (int i = 0; i < FRAME_BUFF_STRIDE * FRAME_BUFF_HEIGHT; i++) { 42 | frame_buff[i] = 0; 43 | } 44 | } 45 | 46 | void update_lcd() { 47 | ws19804::start_write_data(25, 0, FRAME_BUFF_WIDTH, FRAME_BUFF_HEIGHT, frame_buff); 48 | ws19804::finish_write_data(); 49 | } 50 | -------------------------------------------------------------------------------- /samples/font/mono8x16/font2cpp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from PIL import Image 4 | 5 | import sys 6 | import numpy as np 7 | 8 | W = 8 9 | H = 16 10 | PADDING = 1 11 | 12 | COLS = 16 13 | ROWS = 6 14 | LENGTH = COLS * ROWS 15 | 16 | INPUT_IMAGE = sys.argv[1] 17 | NAME_SPACE = sys.argv[2] 18 | 19 | img = Image.open(INPUT_IMAGE) 20 | width, height = img.size 21 | img_pixels = np.array([[img.getpixel((x,y)) for x in range(width)] for y in range(height)]) 22 | 23 | print('#include "stdint.h"') 24 | print() 25 | print('namespace %s {' % NAME_SPACE) 26 | print() 27 | print('const uint8_t bmp[] = {') 28 | 29 | index = 0 30 | 31 | for iy in range(ROWS): 32 | y_step = iy * (H + PADDING) 33 | for ix in range(COLS): 34 | x_step = ix * (W + PADDING) 35 | code = 0x20 + iy * 16 + ix 36 | 37 | for y_sub in range(H): 38 | byte = 0 39 | for x_sub in range(W): 40 | y = y_step + y_sub 41 | x = x_step + x_sub 42 | pixel = img_pixels[y, x] 43 | if pixel[0] == 255: 44 | byte |= 1 << x_sub 45 | 46 | if (index % 16 == 0): 47 | print(' ', end='') 48 | print('0x%02x, ' % byte, end='') 49 | if (index % 16 == 15) or (index == LENGTH - 1): 50 | print() 51 | index += 1 52 | 53 | print('};') 54 | print() 55 | print('}') 56 | -------------------------------------------------------------------------------- /samples/pico_ws19804/ws19804.hpp: -------------------------------------------------------------------------------- 1 | #ifndef WS19804_HPP 2 | #define WS19804_HPP 3 | 4 | #include "stdint.h" 5 | #include "pico/stdlib.h" 6 | #include "hardware/pio.h" 7 | #include "hardware/gpio.h" 8 | #include "hardware/clocks.h" 9 | #include "hardware/dma.h" 10 | 11 | namespace ws19804 { 12 | 13 | static constexpr int WIDTH = 320; 14 | static constexpr int HEIGHT = 240; 15 | static constexpr uint PIN_DC = 8; 16 | static constexpr uint PIN_LCD_CS = 9; 17 | static constexpr uint PIN_TP_CS = 16; 18 | static constexpr uint PIN_SCK = 10; 19 | static constexpr uint PIN_MOSI = 11; 20 | static constexpr uint PIN_MISO = 12; 21 | static constexpr uint PIN_RST = 15; 22 | static constexpr uint PIN_BL = 13; 23 | 24 | enum direction_t { 25 | EMPTY, TX, RX 26 | }; 27 | 28 | // setup GPIO, PIO, LCD 29 | void init(int sys_clk_hz); 30 | 31 | // set SPI clock frequency 32 | void set_spi_speed(int new_speed); 33 | 34 | // clear display 35 | void clear(uint16_t color); 36 | 37 | // start DMA 38 | void start_write_data(int x0, int y0, int w, int h, uint8_t *data); 39 | 40 | // wait DMA to finish 41 | void finish_write_data(); 42 | 43 | // write LCD command 44 | void write_command(uint8_t cmd, const uint8_t *data, int len); 45 | 46 | // write LCD command 47 | void write_command(uint8_t cmd); 48 | 49 | // SPI write 50 | void write_blocking(const uint8_t *data, int len); 51 | 52 | // SPI read 53 | void read_blocking(uint8_t tx_repeat, uint8_t *buff, int len); 54 | 55 | } 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /core/include/shapones/memory.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SHAPONES_MEMORY_HPP 2 | #define SHAPONES_MEMORY_HPP 3 | 4 | #include "shapones/shapones.hpp" 5 | 6 | namespace nes::memory { 7 | 8 | static constexpr int WRAM_SIZE = 0x800; 9 | static constexpr int VRAM_SIZE = 0x1000; 10 | static constexpr int PRGROM_SIZE = 0x8000; 11 | 12 | struct MapperState { 13 | int id; 14 | int chr_bank; 15 | int chr_bank_mask; 16 | }; 17 | 18 | extern const uint8_t *prgrom; 19 | extern const uint8_t *chrrom; 20 | extern uint16_t *chrrom_reordered0; 21 | extern uint16_t *chrrom_reordered1; 22 | 23 | extern uint8_t wram[WRAM_SIZE]; 24 | extern uint8_t vram[VRAM_SIZE]; 25 | 26 | extern addr_t prgrom_addr_mask; 27 | extern addr_t vram_addr_mask; 28 | 29 | extern MapperState mapper; 30 | 31 | bool map_ines(const uint8_t *ines); 32 | 33 | inline uint8_t prgrom_read(addr_t addr) { 34 | return prgrom[addr & prgrom_addr_mask]; 35 | } 36 | 37 | inline uint8_t chrrom_read(addr_t addr) { 38 | addr += mapper.chr_bank * 0x2000; 39 | return chrrom[addr]; 40 | } 41 | 42 | inline uint16_t chrrom_read_w(addr_t addr, bool invert) { 43 | addr += mapper.chr_bank * 0x1000; 44 | if (invert) { 45 | return chrrom_reordered1[addr]; 46 | } 47 | else { 48 | return chrrom_reordered0[addr]; 49 | } 50 | } 51 | 52 | inline uint8_t vram_read(addr_t addr) { 53 | return vram[addr & vram_addr_mask]; 54 | } 55 | 56 | inline void vram_write(addr_t addr, uint8_t value) { 57 | vram[addr & vram_addr_mask] = value; 58 | } 59 | 60 | void ext_write(addr_t addr, uint8_t value); 61 | 62 | } 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /samples/pico_ws19804/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | # Pull in PICO SDK (must be before project) 4 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 5 | include(${PICO_SDK_PATH}/pico_sdk_init.cmake) 6 | 7 | project(pico_nes_ws19804 C CXX ASM) 8 | set(CMAKE_C_STANDARD 11) 9 | set(CMAKE_CXX_STANDARD 17) 10 | 11 | # Initialize the SDK 12 | pico_sdk_init() 13 | 14 | add_executable(pico_nes_ws19804) 15 | 16 | pico_generate_pio_header(pico_nes_ws19804 ${CMAKE_CURRENT_LIST_DIR}/ws19804.pio) 17 | 18 | set(SHAPONES_PATH ../../core) 19 | set(FATFS_PATH ../fatfs/source) 20 | 21 | target_sources( 22 | pico_nes_ws19804 PRIVATE 23 | common.cpp 24 | ws19804.cpp 25 | pico_nes_ws19804.cpp 26 | boot_menu.cpp 27 | ${SHAPONES_PATH}/src/cpu.cpp 28 | ${SHAPONES_PATH}/src/dma.cpp 29 | ${SHAPONES_PATH}/src/input.cpp 30 | ${SHAPONES_PATH}/src/interrupt.cpp 31 | ${SHAPONES_PATH}/src/memory.cpp 32 | ${SHAPONES_PATH}/src/ppu.cpp 33 | ${SHAPONES_PATH}/src/apu.cpp 34 | ${SHAPONES_PATH}/src/shapones.cpp 35 | ${SHAPONES_PATH}/src/utils_std.cpp 36 | ${FATFS_PATH}/ff.c 37 | ${FATFS_PATH}/ffsystem.c 38 | ${FATFS_PATH}/ffunicode.c 39 | ${FATFS_PATH}/mmc_pico_spi.c 40 | ../font/mono8x16/mono8x16.cpp 41 | ../font/mono8x16/mono8x16_bmp.cpp 42 | ) 43 | 44 | target_include_directories( 45 | pico_nes_ws19804 PRIVATE 46 | . 47 | ${SHAPONES_PATH}/include 48 | ${FATFS_PATH} 49 | ../font/mono8x16 50 | ) 51 | 52 | target_link_libraries( 53 | pico_nes_ws19804 PRIVATE 54 | pico_stdlib 55 | hardware_pio 56 | hardware_pwm 57 | hardware_dma 58 | hardware_irq 59 | pico_multicore 60 | ) 61 | 62 | pico_add_extra_outputs(pico_nes_ws19804) 63 | -------------------------------------------------------------------------------- /samples/wxapp/nes_audio.cpp: -------------------------------------------------------------------------------- 1 | #include "nes_audio.hpp" 2 | #include "shapones/shapones.hpp" 3 | 4 | namespace nes_audio { 5 | 6 | static pa_simple *pa; 7 | static std::thread *thread; 8 | static volatile bool continue_flag; 9 | 10 | static void play_thread_func(); 11 | 12 | void play() { 13 | int pa_errno; 14 | 15 | if (thread) stop(); 16 | 17 | pa_sample_spec ss; 18 | ss.format = PA_SAMPLE_U8; 19 | ss.rate = 22050; 20 | ss.channels = 1; 21 | 22 | pa_buffer_attr ba; 23 | ba.maxlength = CHUNK_SIZE * 4; 24 | ba.tlength = CHUNK_SIZE; 25 | ba.prebuf = -1; 26 | ba.minreq = 64; 27 | 28 | pa = pa_simple_new(NULL, "ShapoNES", PA_STREAM_PLAYBACK, NULL, "play", &ss, NULL, &ba, &pa_errno); 29 | if (pa == NULL) { 30 | fprintf(stderr, "ERROR: Failed to connect pulseaudio server: %s\n", pa_strerror(pa_errno)); 31 | return; 32 | } 33 | 34 | nes::apu::set_sampling_rate(ss.rate); 35 | 36 | thread = new std::thread(play_thread_func); 37 | 38 | continue_flag = true; 39 | } 40 | 41 | void stop() { 42 | if ( ! thread) return; 43 | continue_flag = false; 44 | thread->join(); 45 | delete thread; 46 | thread = nullptr; 47 | } 48 | 49 | static void play_thread_func() { 50 | int pa_result, pa_errno; 51 | uint8_t buffer[CHUNK_SIZE]; 52 | 53 | printf("Audio stream opened.\n"); 54 | 55 | while (continue_flag) { 56 | nes::apu::service(buffer, CHUNK_SIZE); 57 | pa_result = pa_simple_write(pa, buffer, CHUNK_SIZE, &pa_errno); 58 | if (pa_result < 0) { 59 | fprintf(stderr, "ERROR: Failed to write data to pulseaudio: %s\n", pa_strerror(pa_errno)); 60 | break; 61 | } 62 | } 63 | 64 | pa_simple_free(pa); 65 | 66 | printf("Audio stream closed.\n"); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /samples/fatfs/LICENSE.txt: -------------------------------------------------------------------------------- 1 | FatFs License 2 | 3 | FatFs has being developped as a personal project of the author, ChaN. It is free from the code anyone else wrote at current release. Following code block shows a copy of the FatFs license document that heading the source files. 4 | 5 | /*----------------------------------------------------------------------------/ 6 | / FatFs - Generic FAT Filesystem Module Rx.xx / 7 | /-----------------------------------------------------------------------------/ 8 | / 9 | / Copyright (C) 20xx, ChaN, all right reserved. 10 | / 11 | / FatFs module is an open source software. Redistribution and use of FatFs in 12 | / source and binary forms, with or without modification, are permitted provided 13 | / that the following condition is met: 14 | / 15 | / 1. Redistributions of source code must retain the above copyright notice, 16 | / this condition and the following disclaimer. 17 | / 18 | / This software is provided by the copyright holder and contributors "AS IS" 19 | / and any warranties related to this software are DISCLAIMED. 20 | / The copyright owner or contributors be NOT LIABLE for any damages caused 21 | / by use of this software. 22 | /----------------------------------------------------------------------------*/ 23 | 24 | Therefore FatFs license is one of the BSD-style licenses, but there is a significant feature. FatFs is mainly intended for embedded systems. In order to extend the usability for commercial products, the redistributions of FatFs in binary form, such as embedded code, binary library and any forms without source code, do not need to include about FatFs in the documentations. This is equivalent to the 1-clause BSD license. Of course FatFs is compatible with the most of open source software licenses include GNU GPL. When you redistribute the FatFs source code with changes or create a fork, the license can also be changed to GNU GPL, BSD-style license or any open source software license that not conflict with FatFs license. 25 | -------------------------------------------------------------------------------- /samples/font/mono8x16/mono8x16.cpp: -------------------------------------------------------------------------------- 1 | #include "mono8x16.hpp" 2 | 3 | namespace mono8x16 { 4 | 5 | static constexpr int W = 8; 6 | static constexpr int H = 16; 7 | static constexpr int CHAR_STRIDE = (W * H + 7) / 8; 8 | 9 | void draw_char_rgb444( 10 | uint8_t *dest, 11 | int dest_stride, 12 | int dest_w, 13 | int dest_h, 14 | int x0, 15 | int y0, 16 | const char c, 17 | uint16_t color 18 | ) { 19 | if (c <= 0x20 || 0x80 <= c) return; 20 | const uint8_t *rd_ptr = bmp + (c - 0x20) * CHAR_STRIDE; 21 | for (int iy = 0; iy < H; iy++) { 22 | int y = y0 + iy; 23 | uint8_t pattern = *(rd_ptr++); 24 | if (0 <= y && y < dest_h) { 25 | for (int ix = 0; ix < W; ix++) { 26 | int x = x0 + ix; 27 | if ((pattern & 1) && 0 <= x && x < dest_w) { 28 | int wr_index = dest_stride * y + x * 3 / 2; 29 | if ((x & 1) == 0) { 30 | dest[wr_index ] = (color >> 4) & 0xff; 31 | dest[wr_index+1] &= 0x0f; 32 | dest[wr_index+1] |= (color << 4) & 0xf0; 33 | } 34 | else { 35 | dest[wr_index ] &= 0xf0; 36 | dest[wr_index ] |= (color >> 8) & 0xf; 37 | dest[wr_index+1] = color & 0xff; 38 | } 39 | } 40 | pattern >>= 1; 41 | } 42 | } 43 | } 44 | } 45 | 46 | void draw_string_rgb444( 47 | uint8_t *dest, 48 | int dest_stride, 49 | int dest_w, 50 | int dest_h, 51 | int x0, 52 | int y0, 53 | const char* str, 54 | uint16_t color 55 | ) { 56 | char c; 57 | while ((c = *(str++)) != '\0') { 58 | draw_char_rgb444( 59 | dest, 60 | dest_stride, 61 | dest_w, 62 | dest_h, 63 | x0, 64 | y0, 65 | c, 66 | color 67 | ); 68 | x0 += W + 1; 69 | } 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /samples/wxapp/nes_app.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "shapones/cpu.hpp" 4 | #include "shapones/utils_std.hpp" 5 | #include "nes_screen.hpp" 6 | #include "nes_audio.hpp" 7 | 8 | enum { 9 | ID_FCFRAME = wxID_HIGHEST, 10 | ID_FCSCREEN, 11 | ID_TIMER 12 | }; 13 | 14 | class FcFrame: public wxFrame { 15 | public: 16 | FcFrame(const wxString &title); 17 | void OnMenuQuit(wxCommandEvent &event); 18 | void OnTimer(wxTimerEvent &event); 19 | void OnClose(wxCloseEvent &event); 20 | 21 | private: 22 | wxMenuBar* menubar; 23 | FcScreen* screen; 24 | wxTimer *timer; 25 | DECLARE_EVENT_TABLE() 26 | }; 27 | 28 | FcFrame::FcFrame(const wxString &title) : 29 | wxFrame(NULL, ID_FCFRAME, title) 30 | { 31 | menubar = new wxMenuBar(); 32 | wxMenu* mFile = new wxMenu(); 33 | mFile->Append(wxID_EXIT, wxT("Quit")); 34 | menubar->Append(mFile, wxT("File")); 35 | SetMenuBar(menubar); 36 | 37 | timer = new wxTimer(this, ID_TIMER); 38 | timer->Start(16); 39 | 40 | screen = new FcScreen(this, ID_FCSCREEN); 41 | 42 | SetTitle(title); 43 | SetClientSize(wxSize(nes::SCREEN_WIDTH * 2, nes::SCREEN_HEIGHT * 2)); 44 | CenterOnScreen(); 45 | 46 | nes_audio::play(); 47 | } 48 | 49 | BEGIN_EVENT_TABLE(FcFrame, wxFrame) 50 | EVT_MENU(wxID_EXIT, FcFrame::OnMenuQuit) 51 | EVT_TIMER(ID_TIMER, FcFrame::OnTimer) 52 | EVT_CLOSE(FcFrame::OnClose) 53 | END_EVENT_TABLE() 54 | 55 | void FcFrame::OnMenuQuit(wxCommandEvent &event) { 56 | Close(); 57 | } 58 | 59 | void FcFrame::OnTimer(wxTimerEvent &event) { 60 | screen->Render(); 61 | } 62 | 63 | void FcFrame::OnClose(wxCloseEvent &event) { 64 | nes_audio::stop(); 65 | event.Skip(); 66 | } 67 | 68 | class FcApp: public wxApp { 69 | public: 70 | FcFrame *frame; 71 | virtual bool OnInit(); 72 | }; 73 | 74 | bool FcApp::OnInit() { 75 | frame = new FcFrame(wxT("ShapoNES")); 76 | nes::load_ines_file(wxApp::argv[1]); 77 | nes::reset(); 78 | frame->Show(true); 79 | 80 | return(true); 81 | } 82 | 83 | DECLARE_APP(FcApp) 84 | IMPLEMENT_APP(FcApp) 85 | -------------------------------------------------------------------------------- /core/include/shapones/cpu.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SHAPONES_CPU_HPP 2 | #define SHAPONES_CPU_HPP 3 | 4 | #include "shapones/shapones.hpp" 5 | 6 | namespace nes::cpu { 7 | 8 | static constexpr int CLOCK_FREQUENCY = 1789773; 9 | 10 | static constexpr addr_t WRAM_BASE = 0x0; 11 | static constexpr addr_t WRAM_MIRROR_BASE = 0x800; 12 | static constexpr addr_t PPUREG_BASE = 0x2000; 13 | static constexpr addr_t OAM_DMA_REG = 0x4014; 14 | static constexpr addr_t INPUT_REG_0 = 0x4016; 15 | static constexpr addr_t INPUT_REG_1 = 0x4017; 16 | static constexpr addr_t PRGROM_BASE = 0x8000; 17 | 18 | static constexpr addr_t VEC_NMI = 0xfffa; 19 | static constexpr addr_t VEC_RESET = 0xfffc; 20 | static constexpr addr_t VEC_IRQ = 0xfffe; 21 | 22 | static constexpr uint8_t STATUS_CARRY = 0x1; 23 | static constexpr uint8_t STATUS_ZERO = 0x2; 24 | static constexpr uint8_t STATUS_INTERRUPT = 0x4; 25 | static constexpr uint8_t STATUS_DECIMALMODE = 0x8; 26 | static constexpr uint8_t STATUS_BREAKMODE = 0x10; 27 | static constexpr uint8_t STATUS_OVERFLOW = 0x40; 28 | static constexpr uint8_t STATUS_NEGATIVE = 0x80; 29 | 30 | struct Registers { 31 | uint8_t A; // accumulator 32 | uint8_t X; // index 33 | uint8_t Y; // index 34 | uint8_t SP; // stack pointer (low byte) 35 | union { // status 36 | uint8_t raw; 37 | struct { 38 | uint8_t carry : 1; 39 | uint8_t zero : 1; 40 | uint8_t interrupt : 1; 41 | uint8_t decimalmode : 1; 42 | uint8_t breakmode : 1; 43 | uint8_t reserved : 1; 44 | uint8_t overflow : 1; 45 | uint8_t negative : 1; 46 | }; 47 | } status; 48 | addr_t PC; // program counter 49 | }; 50 | 51 | enum Mnemonic : uint8_t { 52 | ADC , AHX , ALR , ANC , AND , ARR , ASL , AXS , BCC , BCS , BEQ , BIT , BMI , BNE , BPL , BRK , 53 | BVC , BVS , CLC , CLD , CLI , CLV , CMP , CPX , CPY , DCP , DEC , DEX , DEY , EOR , INC , INX , 54 | INY , ISB , JMP , JSR , LAS , LAX , LDA , LDX , LDY , LSR , NOP , NOPD, NOPI, ORA , PHA , PHP , 55 | PLA , PLP , RLA , ROL , ROR , RRA , RTI , RTS , SAX , SBC , SEC , SED , SEI , SHX , SHY , SLO , 56 | SRE , STA , STX , STY , TAS , TAX , TAY , TSX , TXA , TXS , TYA , XAA 57 | }; 58 | 59 | enum AddrMode : uint8_t { 60 | Absolute , AbsoluteX , AbsoluteY , Accum , 61 | Immediate , Implied , IndAbs , PostIdxInd, 62 | PreIdxInd , Relative , ZeroPage , ZeroPageX , 63 | ZeroPageY 64 | }; 65 | 66 | struct OpDecRecord { 67 | Mnemonic mnemonic; 68 | AddrMode addr_mode; 69 | uint8_t cycle; 70 | }; 71 | 72 | enum RegSel { 73 | A, X, Y 74 | }; 75 | 76 | void service(); 77 | 78 | void reset(); 79 | void stop(); 80 | 81 | uint8_t bus_read(addr_t addr); 82 | void bus_write(addr_t addr, uint8_t data); 83 | 84 | cycle_t ppu_cycle_leading(); 85 | 86 | } 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /core/include/shapones/apu.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SHAPONES_APU_HPP 2 | #define SHAPONES_APU_HPP 3 | 4 | #include "shapones/shapones.hpp" 5 | 6 | namespace nes::apu { 7 | 8 | static constexpr int TIMER_PREC = 16; 9 | 10 | static constexpr addr_t REG_PULSE1_REG0 = 0x4000; 11 | static constexpr addr_t REG_PULSE1_REG1 = 0x4001; 12 | static constexpr addr_t REG_PULSE1_REG2 = 0x4002; 13 | static constexpr addr_t REG_PULSE1_REG3 = 0x4003; 14 | static constexpr addr_t REG_PULSE2_REG0 = 0x4004; 15 | static constexpr addr_t REG_PULSE2_REG1 = 0x4005; 16 | static constexpr addr_t REG_PULSE2_REG2 = 0x4006; 17 | static constexpr addr_t REG_PULSE2_REG3 = 0x4007; 18 | static constexpr addr_t REG_TRIANGLE_REG0 = 0x4008; 19 | static constexpr addr_t REG_TRIANGLE_REG2 = 0x400a; 20 | static constexpr addr_t REG_TRIANGLE_REG3 = 0x400b; 21 | static constexpr addr_t REG_NOISE_REG0 = 0x400c; 22 | static constexpr addr_t REG_NOISE_REG2 = 0x400e; 23 | static constexpr addr_t REG_NOISE_REG3 = 0x400f; 24 | static constexpr addr_t REG_STATUS = 0x4015; 25 | 26 | static constexpr int ENV_FLAG_START = 0x1; 27 | static constexpr int ENV_FLAG_CONSTANT = 0x2; 28 | static constexpr int ENV_FLAG_HALT_LOOP = 0x4; 29 | 30 | static constexpr int SWP_FLAG_ENABLED = 0x1; 31 | static constexpr int SWP_FLAG_NEGATE = 0x2; 32 | 33 | static constexpr int LIN_FLAG_RELOAD = 0x1; 34 | static constexpr int LIN_FLAG_CONTROL = 0x2; 35 | 36 | struct Envelope { 37 | int flags; 38 | int volume; 39 | int divider; 40 | int decay; 41 | }; 42 | 43 | struct Sweep { 44 | int flags; 45 | int period; 46 | int divider; 47 | int shift; 48 | }; 49 | 50 | struct LinearCounter { 51 | bool flags; 52 | int counter; 53 | int reload_value; 54 | }; 55 | 56 | struct PulseState { 57 | uint8_t waveform; 58 | uint32_t timer; 59 | uint32_t timer_period; 60 | uint32_t phase; 61 | int length; 62 | Envelope envelope; 63 | Sweep sweep; 64 | }; 65 | 66 | struct TriangleState { 67 | uint32_t timer; 68 | uint32_t timer_period; 69 | uint32_t phase; 70 | int length; 71 | LinearCounter linear; 72 | }; 73 | 74 | struct NoiseState { 75 | uint16_t lfsr; 76 | int length; 77 | Envelope envelope; 78 | }; 79 | 80 | union Status { 81 | uint8_t raw; 82 | struct { 83 | uint8_t pulse0_enable : 1; 84 | uint8_t pulse1_enable : 1; 85 | uint8_t triangle_enable : 1; 86 | uint8_t noise_enable : 1; 87 | uint8_t dmc_enable : 1; 88 | uint8_t reserved : 1; 89 | uint8_t frame_interrupt : 1; 90 | uint8_t dmc_interrupt : 1; 91 | }; 92 | }; 93 | 94 | void reset(); 95 | uint8_t reg_read(addr_t addr); 96 | void reg_write(addr_t addr, uint8_t value); 97 | void set_sampling_rate(int rate_hz); 98 | void service(uint8_t *buff, int len); 99 | 100 | } 101 | 102 | #endif 103 | -------------------------------------------------------------------------------- /samples/fatfs/source/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 | -------------------------------------------------------------------------------- /samples/pico_ws19804/pwm_audio.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PWM_AUDIO_HPP 2 | #define PWM_AUDIO_HPP 3 | 4 | #include "stdint.h" 5 | #include "hardware/gpio.h" 6 | #include "hardware/pwm.h" 7 | #include "hardware/dma.h" 8 | #include "hardware/clocks.h" 9 | #include "hardware/irq.h" 10 | #include "pico/stdlib.h" 11 | 12 | using DmaFinishdedHandler = void(*)(); 13 | 14 | class PwmAudio; 15 | static PwmAudio *inst; 16 | 17 | class PwmAudio { 18 | public: 19 | using sample_t = uint32_t; 20 | 21 | const dma_channel_transfer_size DMA_WIDTH = 22 | sizeof(sample_t) == 1 ? DMA_SIZE_8 : 23 | sizeof(sample_t) == 2 ? DMA_SIZE_16 : 24 | DMA_SIZE_32; 25 | 26 | const int PIN; 27 | const int SLICE_NUM; 28 | const int SAMPLE_BITS; 29 | const float FREQUENCY; 30 | const int LATENCY; 31 | DmaFinishdedHandler dma_handler; 32 | 33 | sample_t *buff; 34 | int dma_ch; 35 | int playing_bank; 36 | 37 | PwmAudio(int pin, int latency, int sample_bits, float freq_ratio, DmaFinishdedHandler dma_handler) : 38 | PIN(pin), 39 | SLICE_NUM(pwm_gpio_to_slice_num(pin)), 40 | SAMPLE_BITS(sample_bits), 41 | FREQUENCY(freq_ratio), 42 | LATENCY(latency), 43 | buff(new sample_t[latency]), 44 | dma_handler(dma_handler) 45 | { 46 | gpio_set_function(PIN, GPIO_FUNC_PWM); 47 | 48 | int pwm_period = 1 << SAMPLE_BITS; 49 | float pwm_clkdiv = freq_ratio / pwm_period; 50 | 51 | pwm_config pwm_cfg = pwm_get_default_config(); 52 | pwm_config_set_clkdiv(&pwm_cfg, pwm_clkdiv); 53 | pwm_config_set_wrap(&pwm_cfg, pwm_period - 1); 54 | pwm_init(SLICE_NUM, &pwm_cfg, true); 55 | 56 | pwm_set_gpio_level(PIN, 1 << (SAMPLE_BITS-1)); 57 | 58 | inst = this; 59 | } 60 | 61 | void play() { 62 | dma_ch = dma_claim_unused_channel(true); 63 | dma_channel_set_irq0_enabled(dma_ch, true); 64 | irq_set_exclusive_handler(DMA_IRQ_0, dma_handler); 65 | irq_set_enabled(DMA_IRQ_0, true); 66 | 67 | playing_bank = 0; 68 | start_dma(); 69 | } 70 | 71 | void stop() { 72 | dma_channel_unclaim(dma_ch); 73 | pwm_set_gpio_level(PIN, 1 << (SAMPLE_BITS-1)); 74 | } 75 | 76 | void start_dma() { 77 | dma_channel_config dma_cfg = dma_channel_get_default_config(dma_ch); 78 | channel_config_set_transfer_data_size(&dma_cfg, DMA_SIZE_32); 79 | channel_config_set_read_increment(&dma_cfg, true); 80 | channel_config_set_write_increment(&dma_cfg, false); 81 | channel_config_set_dreq(&dma_cfg, DREQ_PWM_WRAP0 + SLICE_NUM); 82 | dma_channel_configure( 83 | dma_ch, 84 | &dma_cfg, 85 | &pwm_hw->slice[SLICE_NUM].cc, // write addr 86 | buff + (playing_bank * LATENCY), // read addr 87 | LATENCY, // number of data 88 | true // start immediately 89 | ); 90 | } 91 | 92 | void flip_buffer() { 93 | int fill_bank = playing_bank; 94 | playing_bank = (playing_bank + 1) & 1; 95 | inst->start_dma(); 96 | dma_hw->ints0 = (1u << dma_ch); 97 | } 98 | 99 | sample_t* get_buffer(int bank) { 100 | return buff + (bank * LATENCY); 101 | } 102 | 103 | sample_t* get_next_buffer() { 104 | int next_bank = (playing_bank + 1) & 1; 105 | return get_buffer(next_bank); 106 | } 107 | }; 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /samples/pico_ws19804/boot_menu.cpp: -------------------------------------------------------------------------------- 1 | #include "boot_menu.hpp" 2 | 3 | #include 4 | 5 | #include "ws19804.hpp" 6 | #include "mono8x16.hpp" 7 | #include "common.hpp" 8 | 9 | #include "ff.h" 10 | #include "diskio.h" 11 | 12 | constexpr int MAX_FILES = 64; 13 | 14 | // enumerate NES files 15 | static int enum_files(FATFS *fs, char **fname_list, int *fsize_list); 16 | 17 | // show ROM select menu 18 | static int rom_select(int num_files, char **file_list); 19 | 20 | // load NES file 21 | static bool load_nes(const char *fname, int size); 22 | 23 | bool boot_menu() { 24 | FATFS fs; 25 | char *fname_list[MAX_FILES]; 26 | int fsize_list[MAX_FILES]; 27 | int num_files = enum_files(&fs, fname_list, fsize_list); 28 | if (num_files < 0) { 29 | return false; 30 | } 31 | 32 | int index = rom_select(num_files, fname_list); 33 | 34 | if ( ! load_nes(fname_list[index], fsize_list[index])) { 35 | return false; 36 | } 37 | 38 | for (int i = 0; i < num_files; i++) { 39 | free(fname_list[i]); 40 | } 41 | 42 | return true; 43 | } 44 | 45 | static int enum_files(FATFS *fs, char **fname_list, int *fsize_list) { 46 | char tmp[16]; 47 | int y = 20; 48 | constexpr int x_result = 120; 49 | 50 | DSTATUS dsts; 51 | FRESULT fres; 52 | 53 | clear_frame_buff(); 54 | 55 | // Init FatFS 56 | draw_string(0, y, "Init FatFS"); update_lcd(); 57 | dsts = disk_initialize(0); 58 | if (dsts & STA_NOINIT) { 59 | sprintf(tmp, "[NG] code=0x%x", (int)dsts); 60 | draw_string(x_result, y, tmp); update_lcd(); 61 | return -1; 62 | } 63 | draw_string(x_result, y, "[OK]"); update_lcd(); 64 | y += 20; 65 | 66 | // Mount 67 | draw_string(0, y, "Disk Mount"); update_lcd(); 68 | fres = f_mount(fs, "", 0); 69 | if (fres != FR_OK) { 70 | sprintf(tmp, "[NG] code=0x%x", (int)fres); 71 | draw_string(x_result, y, tmp); update_lcd(); 72 | return -1; 73 | } 74 | draw_string(x_result, y, "[OK]"); update_lcd(); 75 | y += 20; 76 | 77 | // Enumerate File 78 | draw_string(0, y, "File List"); update_lcd(); 79 | 80 | DIR dobj; 81 | FILINFO finfo; 82 | fres = f_findfirst(&dobj, &finfo, "", "*.nes"); 83 | int num_files = 0; 84 | while (fres == FR_OK && finfo.fname[0]) { 85 | fname_list[num_files] = (char*)malloc(strlen(finfo.fname) + 1); 86 | strcpy(fname_list[num_files], finfo.fname); 87 | fsize_list[num_files] = finfo.fsize; 88 | fres = f_findnext(&dobj, &finfo); 89 | num_files++; 90 | } 91 | if (fres != FR_OK) { 92 | sprintf(tmp, "[NG] code=0x%x", (int)fres); 93 | draw_string(x_result, y, tmp); update_lcd(); 94 | return -1; 95 | } 96 | f_closedir(&dobj); 97 | draw_string(x_result, y, "[OK]"); update_lcd(); 98 | y += 20; 99 | 100 | return num_files; 101 | } 102 | 103 | static int rom_select(int num_files, char **file_list) { 104 | int sel_index = 0; 105 | 106 | for(;;) { 107 | clear_frame_buff(); 108 | for(int i = 0; i < num_files; i++) { 109 | draw_string(20, i * 20, file_list[i]); 110 | } 111 | draw_string(0, sel_index * 20, "=>"); 112 | update_lcd(); 113 | 114 | switch(wait_key()) { 115 | case nes::input::BTN_UP: 116 | sel_index = (sel_index + num_files - 1) % num_files; 117 | break; 118 | case nes::input::BTN_DOWN: 119 | sel_index = (sel_index + 1) % num_files; 120 | break; 121 | case nes::input::BTN_A: 122 | case nes::input::BTN_START: 123 | return sel_index; 124 | } 125 | } 126 | } 127 | 128 | static bool load_nes(const char *fname, int size) { 129 | FIL fil; 130 | FRESULT fr; 131 | 132 | clear_frame_buff(); 133 | draw_string(0, 0, "Loading..."); 134 | update_lcd(); 135 | 136 | fr = f_open(&fil, fname, FA_READ); 137 | if (fr) { 138 | draw_string(0, 20, "File open failed."); 139 | update_lcd(); 140 | return false; 141 | } 142 | 143 | UINT sz; 144 | uint8_t *ines = (uint8_t*)malloc(size); 145 | fr = f_read(&fil, ines, size, &sz); 146 | if (fr) { 147 | draw_string(0, 20, "File read failed."); 148 | update_lcd(); 149 | return false; 150 | } 151 | 152 | f_close(&fil); 153 | 154 | nes::memory::map_ines(ines); 155 | ws19804::set_spi_speed(SYS_CLK_FREQ / 4); 156 | 157 | return true; 158 | } 159 | -------------------------------------------------------------------------------- /samples/wxapp/nes_screen.cpp: -------------------------------------------------------------------------------- 1 | #include "stdlib.h" 2 | #include 3 | #include "shapones/shapones.hpp" 4 | #include "nes_screen.hpp" 5 | 6 | static const uint32_t colors[] = { 7 | 0x808080, 0x003DA6, 0x0012B0, 0x440096, 8 | 0xA1005E, 0xC70028, 0xBA0600, 0x8C1700, 9 | 0x5C2F00, 0x104500, 0x054A00, 0x00472E, 10 | 0x004166, 0x000000, 0x050505, 0x050505, 11 | 0xC7C7C7, 0x0077FF, 0x2155FF, 0x8237FA, 12 | 0xEB2FB5, 0xFF2950, 0xFF2200, 0xD63200, 13 | 0xC46200, 0x358000, 0x058F00, 0x008A55, 14 | 0x0099CC, 0x212121, 0x090909, 0x090909, 15 | 0xFFFFFF, 0x0FD7FF, 0x69A2FF, 0xD480FF, 16 | 0xFF45F3, 0xFF618B, 0xFF8833, 0xFF9C12, 17 | 0xFABC20, 0x9FE30E, 0x2BF035, 0x0CF0A4, 18 | 0x05FBFF, 0x5E5E5E, 0x0D0D0D, 0x0D0D0D, 19 | 0xFFFFFF, 0xA6FCFF, 0xB3ECFF, 0xDAABEB, 20 | 0xFFA8F9, 0xFFABB3, 0xFFD2B0, 0xFFEFA6, 21 | 0xFFF79C, 0xD7E895, 0xA6EDAF, 0xA2F2DA, 22 | 0x99FFFC, 0xDDDDDD, 0x111111, 0x111111, 23 | }; 24 | 25 | static double total_emu_time_ms = 0; 26 | static int total_frames = 0; 27 | 28 | static double get_time_ms() { 29 | auto now = std::chrono::system_clock::now().time_since_epoch(); 30 | auto ns = std::chrono::duration_cast(now).count(); 31 | return (double)ns / 1000000; 32 | } 33 | 34 | FcScreen::FcScreen(wxFrame *parent, wxWindowID id) : 35 | wxScrolledWindow(parent, id, wxDefaultPosition, wxDefaultSize, wxNO_FULL_REPAINT_ON_RESIZE) 36 | { 37 | owner = parent; 38 | SetBackgroundColour(*wxBLACK); 39 | frame_buff = wxImage(nes::SCREEN_WIDTH * 2, nes::SCREEN_HEIGHT * 2); 40 | } 41 | 42 | BEGIN_EVENT_TABLE(FcScreen, wxScrolledWindow) 43 | EVT_PAINT(FcScreen::OnPaint) 44 | EVT_KEY_DOWN(FcScreen::OnKeyDown) 45 | EVT_KEY_UP(FcScreen::OnKeyUp) 46 | END_EVENT_TABLE(); 47 | 48 | void FcScreen::Render() { 49 | static int itvl = 0; 50 | bool upd = (((itvl++) & 0x3) == 0); 51 | uint8_t line_buff[nes::SCREEN_WIDTH]; 52 | 53 | nes::vsync(line_buff); 54 | auto wr_ptr = frame_buff.GetData(); 55 | for (int y = 0; y < nes::SCREEN_HEIGHT; y++) { 56 | auto t_start = get_time_ms(); 57 | nes::render_next_line(line_buff); 58 | total_emu_time_ms += get_time_ms() - t_start; 59 | 60 | auto rd_ptr = line_buff; 61 | for (int x = 0; x < nes::SCREEN_WIDTH; x++) { 62 | int color_index = *(rd_ptr++) & 0x3f; 63 | uint32_t color = colors[color_index]; 64 | *(wr_ptr++) = (color >> 16) & 0xff; 65 | *(wr_ptr++) = (color >> 8) & 0xff; 66 | *(wr_ptr++) = color & 0xff; 67 | *(wr_ptr++) = (color >> 16) & 0xff; 68 | *(wr_ptr++) = (color >> 8) & 0xff; 69 | *(wr_ptr++) = color & 0xff; 70 | } 71 | size_t line_length = 3 * 2 * nes::SCREEN_WIDTH; 72 | memcpy(wr_ptr, wr_ptr - line_length, line_length); 73 | wr_ptr += line_length; 74 | } 75 | 76 | total_frames++; 77 | 78 | Refresh(); 79 | //if (upd) wxYield(); 80 | } 81 | 82 | void FcScreen::OnPaint(wxPaintEvent &event) { 83 | wxPaintDC pdc(this); 84 | 85 | auto ptr = frame_buff.GetData(); 86 | 87 | wxBitmap bmp = wxBitmap(frame_buff); 88 | pdc.DrawBitmap(bmp, 0, 0, false); 89 | 90 | char s[32]; 91 | sprintf(s, "%6.3lfms / frame", total_emu_time_ms / total_frames); 92 | pdc.SetTextForeground(*wxWHITE); 93 | pdc.DrawText(wxString(s), 0, 0); 94 | } 95 | 96 | void FcScreen::OnKeyDown(wxKeyEvent &event) { 97 | auto input0 = nes::input::get_raw(0); 98 | switch (event.GetKeyCode()) { 99 | case 'Z': input0.A = 1; break; 100 | case 'X': input0.B = 1; break; 101 | case 'C': input0.start = 1; break; 102 | case 'V': input0.select = 1; break; 103 | case wxKeyCode::WXK_UP: input0.up = 1; break; 104 | case wxKeyCode::WXK_DOWN: input0.down = 1; break; 105 | case wxKeyCode::WXK_LEFT: input0.left = 1; break; 106 | case wxKeyCode::WXK_RIGHT: input0.right = 1; break; 107 | } 108 | nes::input::set_raw(0, input0); 109 | } 110 | 111 | void FcScreen::OnKeyUp(wxKeyEvent &event) { 112 | auto input0 = nes::input::get_raw(0); 113 | switch (event.GetKeyCode()){ 114 | case 'Z': input0.A = 0; break; 115 | case 'X': input0.B = 0; break; 116 | case 'C': input0.start = 0; break; 117 | case 'V': input0.select = 0; break; 118 | case wxKeyCode::WXK_UP: input0.up = 0; break; 119 | case wxKeyCode::WXK_DOWN: input0.down = 0; break; 120 | case wxKeyCode::WXK_LEFT: input0.left = 0; break; 121 | case wxKeyCode::WXK_RIGHT: input0.right = 0; break; 122 | case wxKeyCode::WXK_ESCAPE: owner->Close(); break; 123 | } 124 | nes::input::set_raw(0, input0); 125 | } 126 | -------------------------------------------------------------------------------- /core/include/shapones/ppu.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SHAPONES_PPU_HPP 2 | #define SHAPONES_PPU_HPP 3 | 4 | #include "shapones/shapones.hpp" 5 | 6 | namespace nes::ppu { 7 | 8 | static constexpr cycle_t MAX_DELAY_CYCLES = 128; 9 | 10 | static constexpr int TILE_SIZE = 8; 11 | static constexpr int NUM_TILE_X = SCREEN_WIDTH / TILE_SIZE; 12 | static constexpr int NUM_TILE_Y = SCREEN_HEIGHT / TILE_SIZE; 13 | static constexpr int BLOCK_SIZE = TILE_SIZE * 2; 14 | 15 | static constexpr int REG_SIZE = 8; 16 | static constexpr addr_t REG_PPUCTRL = 0x2000; 17 | static constexpr addr_t REG_PPUMASK = 0x2001; 18 | static constexpr addr_t REG_PPUSTATUS = 0x2002; 19 | static constexpr addr_t REG_OAMADDR = 0x2003; 20 | static constexpr addr_t REG_OAMDATA = 0x2004; 21 | static constexpr addr_t REG_PPUSCROLL = 0x2005; 22 | static constexpr addr_t REG_PPUADDR = 0x2006; 23 | static constexpr addr_t REG_PPUDATA = 0x2007; 24 | 25 | static constexpr int PALETTE_SIZE = 4; 26 | static constexpr int PALETTE_NUM_BANK = 8; 27 | static constexpr int PALETTE_FILE_SIZE = PALETTE_SIZE * PALETTE_NUM_BANK; 28 | static constexpr int PALETTE_FILE_SIZE_WITH_MIRROR = 0x100; 29 | 30 | static constexpr uint8_t OPAQUE_FLAG = 0x80; 31 | 32 | static constexpr addr_t CHRROM_BASE = 0x0000; 33 | static constexpr addr_t CHRROM_SIZE = 0x2000; 34 | static constexpr addr_t VRAM_BASE = 0x2000; 35 | static constexpr addr_t VRAM_MIRROR_BASE = 0x3000; 36 | static constexpr addr_t VRAM_MIRROR_SIZE = 0xf00; 37 | static constexpr addr_t PALETTE_FILE_BASE = 0x3f00; 38 | 39 | static constexpr addr_t OAM_DMA_SRC_ADDR = 0x200; 40 | 41 | static constexpr int MAX_VISIBLE_SPRITES = 8; 42 | static constexpr int SPRITE_Y_OFFSET = 1; 43 | 44 | static constexpr int LINE_CYCLES = 341; 45 | static constexpr int SCAN_LINES = 262; 46 | 47 | static constexpr int NAME_LINE_STRIDE = SCREEN_WIDTH / 8; 48 | static constexpr int NAME_PAGE_STRIDE = 0x400; 49 | 50 | static constexpr int MAX_SPRITE_COUNT = 64; 51 | static constexpr int OAM_SIZE = MAX_SPRITE_COUNT * 4; 52 | 53 | static constexpr uint16_t SCROLL_MASK_COARSE_X = 0x001fu; 54 | static constexpr uint16_t SCROLL_MASK_COARSE_Y = 0x03e0u; 55 | static constexpr uint16_t SCROLL_MASK_NAME_SEL = 0x0c00u; 56 | static constexpr uint16_t SCROLL_MASK_FINE_Y = 0x7000u; 57 | static constexpr uint16_t SCROLL_MASK_PPU_ADDR = 0x3fffu; 58 | 59 | struct Registers { 60 | public: 61 | 62 | // Control Register 63 | union { 64 | uint8_t raw; 65 | struct { 66 | uint8_t unused : 2; // [1:0] 67 | uint8_t incr_stride : 1; // [2] addr increment (0:+1, 1:+32) 68 | uint8_t sprite_name_sel : 1; // [3] sprite pattern sel (0:0x0000, 1:0x1000) 69 | uint8_t bg_name_sel : 1; // [4] BG pattern sel (0:0x0000, 1:0x1000) 70 | uint8_t sprite_size : 1; // [5] sprite size (0:8x8, 1:8x16) 71 | uint8_t ppu_master : 1; // [6] ppu master/slave 72 | uint8_t vblank_irq_enable : 1; // [7] vblank interrupt enable 73 | }; 74 | } control; 75 | 76 | // Mask Register 77 | union { 78 | uint8_t raw; 79 | struct { 80 | uint8_t color : 1; // [0] 0:color, 1:mono 81 | uint8_t bg_clip : 1; // [1] BG clipping 82 | uint8_t sprite_clip : 1; // [2] sprite clipping 83 | uint8_t bg_enable : 1; // [3] BG enable 84 | uint8_t sprite_enable : 1; // [4] sprite enable 85 | uint8_t emphasis_r : 1; // [5] emphasis R 86 | uint8_t emphasis_g : 1; // [6] emphasis G 87 | uint8_t emphasis_b : 1; // [7] emphasis B 88 | }; 89 | } mask; 90 | 91 | // Status Register 92 | union { 93 | uint8_t raw; 94 | struct { 95 | uint8_t reserved : 5; // [4:0] 96 | uint8_t overflow : 1; // [5] overflow (unused) 97 | uint8_t sprite0_hit : 1; // [6] sprite 0 hit 98 | uint8_t vblank_flag : 1; // [7] vblank flag 99 | }; 100 | } status; 101 | 102 | // OAM Indirect Address 103 | uint8_t oam_addr; 104 | 105 | // Scroll Registers 106 | uint8_t fine_x; 107 | uint16_t scroll; 108 | }; 109 | 110 | static constexpr uint8_t OAM_ATTR_PALETTE = 0x3; 111 | static constexpr uint8_t OAM_ATTR_PRIORITY = 0x20; 112 | static constexpr uint8_t OAM_ATTR_INVERT_H = 0x40; 113 | static constexpr uint8_t OAM_ATTR_INVERT_V = 0x80; 114 | 115 | union Palette { 116 | uint32_t packed; 117 | uint8_t color[4]; 118 | }; 119 | 120 | struct OamEntry { 121 | uint8_t y; 122 | uint8_t tile; 123 | uint8_t attr; 124 | uint8_t x; 125 | }; 126 | 127 | static constexpr uint8_t SL_ATTR_BEHIND = 0x1; 128 | static constexpr uint8_t SL_ATTR_ZERO = 0x2; 129 | 130 | struct SpriteLine { 131 | uint8_t x; 132 | uint8_t attr; 133 | uint16_t chr; 134 | Palette palette; 135 | }; 136 | 137 | static constexpr int FOCUS_HBLANK = 1; 138 | static constexpr int FOCUS_VBLANK = 2; 139 | static constexpr int FOCUS_1STLINE = 3; 140 | 141 | void reset(); 142 | bool is_in_hblank(); 143 | int current_focus_y(); 144 | 145 | uint8_t reg_read(addr_t addr); 146 | void reg_write(addr_t addr, uint8_t data); 147 | 148 | void oam_dma_write(addr_t offset, uint8_t data); 149 | 150 | bool service(uint8_t *line_buff); 151 | 152 | cycle_t cycle_following(); 153 | 154 | } 155 | 156 | #endif 157 | -------------------------------------------------------------------------------- /core/src/memory.cpp: -------------------------------------------------------------------------------- 1 | #include "shapones/shapones.hpp" 2 | 3 | namespace nes::memory { 4 | 5 | const uint8_t *prgrom; 6 | const uint8_t *chrrom; 7 | uint16_t *chrrom_reordered0; 8 | uint16_t *chrrom_reordered1; 9 | 10 | uint8_t wram[WRAM_SIZE]; 11 | uint8_t vram[VRAM_SIZE]; 12 | 13 | addr_t prgrom_addr_mask = PRGROM_SIZE - 1; 14 | addr_t vram_addr_mask = VRAM_SIZE - 1; 15 | 16 | MapperState mapper; 17 | 18 | bool map_ines(const uint8_t *ines) { 19 | // iNES file format 20 | // https://www.nesdev.org/wiki/INES 21 | 22 | // marker 23 | if (ines[0] != 0x4e && ines[1] != 0x45 && ines[2] != 0x53 && ines[3] != 0x1a) { 24 | return false; 25 | } 26 | 27 | // Size of PRG ROM in 16 KB units 28 | int num_prg_rom_pages = ines[4]; 29 | switch (num_prg_rom_pages) { 30 | case 1: prgrom_addr_mask = 0x3fff; break; 31 | case 2: prgrom_addr_mask = 0x7fff; break; 32 | default: 33 | NES_ERRORF("Unsupported number of PRGROM pages: %d\n", num_prg_rom_pages); 34 | } 35 | 36 | NES_PRINTF("Number of PRGROM pages = %d (%dkB)\n", num_prg_rom_pages, num_prg_rom_pages * 16); 37 | 38 | // Size of CHR ROM in 8 KB units 39 | int num_chr_rom_pages = ines[5]; 40 | NES_PRINTF("Number of CHRROM pages = %d (%dkB)\n", num_chr_rom_pages, num_chr_rom_pages * 8); 41 | 42 | uint8_t flags6 = ines[6]; 43 | NES_PRINTF("Flags6 = 0x%02x\n", (int)flags6); 44 | // 512-byte trainer at $7000-$71FF (stored before PRG data) 45 | bool has_trainer = (flags6 & 0x2) != 0; 46 | 47 | if (flags6 & 0x8) { 48 | // Ignore mirroring control or above mirroring bit; instead provide four-screen VRAM 49 | vram_addr_mask = VRAM_SIZE - 1; 50 | NES_PRINTF("Mirroring = NONE (vram_addr_mask=%x)\n", vram_addr_mask); 51 | } 52 | else if (flags6 & 0x1) { 53 | vram_addr_mask = (VRAM_SIZE - 1) - (VRAM_SIZE / 2); 54 | NES_PRINTF("Mirroring = VERTICAL (vram_addr_mask=%x)\n", vram_addr_mask); 55 | } 56 | else { 57 | vram_addr_mask = (VRAM_SIZE - 1) - (VRAM_SIZE / 4); 58 | NES_PRINTF("Mirroring = HORIZONTAL (vram_addr_mask=%x)\n", vram_addr_mask); 59 | } 60 | 61 | uint8_t flags7 = ines[7]; 62 | 63 | mapper.id = (flags7 & 0xf0) | ((flags6 >> 4) & 0xf); 64 | NES_PRINTF("Mapper No.%d\n", mapper.id); 65 | switch(mapper.id) { 66 | case 0: 67 | mapper.chr_bank_mask = 0; 68 | break; 69 | case 3: 70 | mapper.chr_bank_mask = num_chr_rom_pages - 1; 71 | break; 72 | default: 73 | NES_ERRORF("Unsupported Mapper Number\n"); 74 | break; 75 | } 76 | mapper.chr_bank = 0; 77 | NES_PRINTF(" CHRROM bank mask = 0x%x\n", mapper.chr_bank_mask); 78 | 79 | int start_of_prg_rom = 0x10; 80 | if (has_trainer) start_of_prg_rom += 0x200; 81 | prgrom = ines + start_of_prg_rom; 82 | 83 | int start_of_chr_rom = start_of_prg_rom + num_prg_rom_pages * 0x4000; 84 | chrrom = ines + start_of_chr_rom; 85 | 86 | // reorder CHRROM bits for fast access 87 | int num_chars = num_chr_rom_pages * 0x2000 / 16; 88 | chrrom_reordered0 = new uint16_t[num_chars * 8]; 89 | chrrom_reordered1 = new uint16_t[num_chars * 8]; 90 | for (int ic = 0; ic < num_chars; ic++) { 91 | for (int iy = 0; iy < 8; iy++) { 92 | uint8_t chr0 = chrrom[ic * 16 + iy]; 93 | uint8_t chr1 = chrrom[ic * 16 + iy + 8]; 94 | 95 | { 96 | uint16_t chr = 0; 97 | chr = (uint16_t)(chr1 & 0x01) << 15; 98 | chr |= (uint16_t)(chr0 & 0x01) << 14; 99 | chr |= (uint16_t)(chr1 & 0x02) << 12; 100 | chr |= (uint16_t)(chr0 & 0x02) << 11; 101 | chr |= (uint16_t)(chr1 & 0x04) << 9; 102 | chr |= (uint16_t)(chr0 & 0x04) << 8; 103 | chr |= (uint16_t)(chr1 & 0x08) << 6; 104 | chr |= (uint16_t)(chr0 & 0x08) << 5; 105 | chr |= (uint16_t)(chr1 & 0x10) << 3; 106 | chr |= (uint16_t)(chr0 & 0x10) << 2; 107 | chr |= (uint16_t)(chr1 & 0x20); 108 | chr |= (uint16_t)(chr0 & 0x20) >> 1; 109 | chr |= (uint16_t)(chr1 & 0x40) >> 3; 110 | chr |= (uint16_t)(chr0 & 0x40) >> 4; 111 | chr |= (uint16_t)(chr1 & 0x80) >> 6; 112 | chr |= (uint16_t)(chr0 & 0x80) >> 7; 113 | chrrom_reordered0[ic * 8 + iy] = chr; 114 | } 115 | 116 | { 117 | uint16_t chr = 0; 118 | chr = (uint16_t)(chr1 & 0x80) << 8; 119 | chr |= (uint16_t)(chr0 & 0x80) << 7; 120 | chr |= (uint16_t)(chr1 & 0x40) << 7; 121 | chr |= (uint16_t)(chr0 & 0x40) << 6; 122 | chr |= (uint16_t)(chr1 & 0x20) << 6; 123 | chr |= (uint16_t)(chr0 & 0x20) << 5; 124 | chr |= (uint16_t)(chr1 & 0x10) << 5; 125 | chr |= (uint16_t)(chr0 & 0x10) << 4; 126 | chr |= (uint16_t)(chr1 & 0x08) << 4; 127 | chr |= (uint16_t)(chr0 & 0x08) << 3; 128 | chr |= (uint16_t)(chr1 & 0x04) << 3; 129 | chr |= (uint16_t)(chr0 & 0x04) << 2; 130 | chr |= (uint16_t)(chr1 & 0x02) << 2; 131 | chr |= (uint16_t)(chr0 & 0x02) << 1; 132 | chr |= (uint16_t)(chr1 & 0x01) << 1; 133 | chr |= (uint16_t)(chr0 & 0x01); 134 | chrrom_reordered1[ic * 8 + iy] = chr; 135 | } 136 | } 137 | } 138 | 139 | return true; 140 | } 141 | 142 | void ext_write(addr_t addr, uint8_t value) { 143 | switch(mapper.id) { 144 | case 3: 145 | if (0x8000 <= addr && addr <= 0xffff) { 146 | mapper.chr_bank = value & mapper.chr_bank_mask; 147 | } 148 | } 149 | } 150 | 151 | } 152 | -------------------------------------------------------------------------------- /samples/fatfs/source/diskio.c: -------------------------------------------------------------------------------- 1 | /*-----------------------------------------------------------------------*/ 2 | /* Low level disk I/O module SKELETON for FatFs (C)ChaN, 2019 */ 3 | /*-----------------------------------------------------------------------*/ 4 | /* If a working storage control module is available, it should be */ 5 | /* attached to the FatFs via a glue function rather than modifying it. */ 6 | /* This is an example of glue functions to attach various exsisting */ 7 | /* storage control modules to the FatFs module with a defined API. */ 8 | /*-----------------------------------------------------------------------*/ 9 | 10 | #include "ff.h" /* Obtains integer types */ 11 | #include "diskio.h" /* Declarations of disk functions */ 12 | 13 | /* Definitions of physical drive number for each drive */ 14 | #define DEV_RAM 0 /* Example: Map Ramdisk to physical drive 0 */ 15 | #define DEV_MMC 1 /* Example: Map MMC/SD card to physical drive 1 */ 16 | #define DEV_USB 2 /* Example: Map USB MSD to physical drive 2 */ 17 | 18 | 19 | /*-----------------------------------------------------------------------*/ 20 | /* Get Drive Status */ 21 | /*-----------------------------------------------------------------------*/ 22 | 23 | DSTATUS disk_status ( 24 | BYTE pdrv /* Physical drive nmuber to identify the drive */ 25 | ) 26 | { 27 | DSTATUS stat; 28 | int result; 29 | 30 | switch (pdrv) { 31 | case DEV_RAM : 32 | result = RAM_disk_status(); 33 | 34 | // translate the reslut code here 35 | 36 | return stat; 37 | 38 | case DEV_MMC : 39 | result = MMC_disk_status(); 40 | 41 | // translate the reslut code here 42 | 43 | return stat; 44 | 45 | case DEV_USB : 46 | result = USB_disk_status(); 47 | 48 | // translate the reslut code here 49 | 50 | return stat; 51 | } 52 | return STA_NOINIT; 53 | } 54 | 55 | 56 | 57 | /*-----------------------------------------------------------------------*/ 58 | /* Inidialize a Drive */ 59 | /*-----------------------------------------------------------------------*/ 60 | 61 | DSTATUS disk_initialize ( 62 | BYTE pdrv /* Physical drive nmuber to identify the drive */ 63 | ) 64 | { 65 | DSTATUS stat; 66 | int result; 67 | 68 | switch (pdrv) { 69 | case DEV_RAM : 70 | result = RAM_disk_initialize(); 71 | 72 | // translate the reslut code here 73 | 74 | return stat; 75 | 76 | case DEV_MMC : 77 | result = MMC_disk_initialize(); 78 | 79 | // translate the reslut code here 80 | 81 | return stat; 82 | 83 | case DEV_USB : 84 | result = USB_disk_initialize(); 85 | 86 | // translate the reslut code here 87 | 88 | return stat; 89 | } 90 | return STA_NOINIT; 91 | } 92 | 93 | 94 | 95 | /*-----------------------------------------------------------------------*/ 96 | /* Read Sector(s) */ 97 | /*-----------------------------------------------------------------------*/ 98 | 99 | DRESULT disk_read ( 100 | BYTE pdrv, /* Physical drive nmuber to identify the drive */ 101 | BYTE *buff, /* Data buffer to store read data */ 102 | LBA_t sector, /* Start sector in LBA */ 103 | UINT count /* Number of sectors to read */ 104 | ) 105 | { 106 | DRESULT res; 107 | int result; 108 | 109 | switch (pdrv) { 110 | case DEV_RAM : 111 | // translate the arguments here 112 | 113 | result = RAM_disk_read(buff, sector, count); 114 | 115 | // translate the reslut code here 116 | 117 | return res; 118 | 119 | case DEV_MMC : 120 | // translate the arguments here 121 | 122 | result = MMC_disk_read(buff, sector, count); 123 | 124 | // translate the reslut code here 125 | 126 | return res; 127 | 128 | case DEV_USB : 129 | // translate the arguments here 130 | 131 | result = USB_disk_read(buff, sector, count); 132 | 133 | // translate the reslut code here 134 | 135 | return res; 136 | } 137 | 138 | return RES_PARERR; 139 | } 140 | 141 | 142 | 143 | /*-----------------------------------------------------------------------*/ 144 | /* Write Sector(s) */ 145 | /*-----------------------------------------------------------------------*/ 146 | 147 | #if FF_FS_READONLY == 0 148 | 149 | DRESULT disk_write ( 150 | BYTE pdrv, /* Physical drive nmuber to identify the drive */ 151 | const BYTE *buff, /* Data to be written */ 152 | LBA_t sector, /* Start sector in LBA */ 153 | UINT count /* Number of sectors to write */ 154 | ) 155 | { 156 | DRESULT res; 157 | int result; 158 | 159 | switch (pdrv) { 160 | case DEV_RAM : 161 | // translate the arguments here 162 | 163 | result = RAM_disk_write(buff, sector, count); 164 | 165 | // translate the reslut code here 166 | 167 | return res; 168 | 169 | case DEV_MMC : 170 | // translate the arguments here 171 | 172 | result = MMC_disk_write(buff, sector, count); 173 | 174 | // translate the reslut code here 175 | 176 | return res; 177 | 178 | case DEV_USB : 179 | // translate the arguments here 180 | 181 | result = USB_disk_write(buff, sector, count); 182 | 183 | // translate the reslut code here 184 | 185 | return res; 186 | } 187 | 188 | return RES_PARERR; 189 | } 190 | 191 | #endif 192 | 193 | 194 | /*-----------------------------------------------------------------------*/ 195 | /* Miscellaneous Functions */ 196 | /*-----------------------------------------------------------------------*/ 197 | 198 | DRESULT disk_ioctl ( 199 | BYTE pdrv, /* Physical drive nmuber (0..) */ 200 | BYTE cmd, /* Control code */ 201 | void *buff /* Buffer to send/receive control data */ 202 | ) 203 | { 204 | DRESULT res; 205 | int result; 206 | 207 | switch (pdrv) { 208 | case DEV_RAM : 209 | 210 | // Process of the command for the RAM drive 211 | 212 | return res; 213 | 214 | case DEV_MMC : 215 | 216 | // Process of the command for the MMC/SD card 217 | 218 | return res; 219 | 220 | case DEV_USB : 221 | 222 | // Process of the command the USB drive 223 | 224 | return res; 225 | } 226 | 227 | return RES_PARERR; 228 | } 229 | 230 | -------------------------------------------------------------------------------- /samples/fatfs/source/ffsystem.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------*/ 2 | /* A Sample Code of User Provided OS Dependent Functions for FatFs */ 3 | /*------------------------------------------------------------------------*/ 4 | 5 | #include "ff.h" 6 | 7 | 8 | #if FF_USE_LFN == 3 /* Use dynamic memory allocation */ 9 | 10 | /*------------------------------------------------------------------------*/ 11 | /* Allocate/Free a Memory Block */ 12 | /*------------------------------------------------------------------------*/ 13 | 14 | #include /* with POSIX API */ 15 | 16 | 17 | void* ff_memalloc ( /* Returns pointer to the allocated memory block (null if not enough core) */ 18 | UINT msize /* Number of bytes to allocate */ 19 | ) 20 | { 21 | return malloc((size_t)msize); /* Allocate a new memory block */ 22 | } 23 | 24 | 25 | void ff_memfree ( 26 | void* mblock /* Pointer to the memory block to free (no effect if null) */ 27 | ) 28 | { 29 | free(mblock); /* Free the memory block */ 30 | } 31 | 32 | #endif 33 | 34 | 35 | 36 | 37 | #if FF_FS_REENTRANT /* Mutal exclusion */ 38 | /*------------------------------------------------------------------------*/ 39 | /* Definitions of Mutex */ 40 | /*------------------------------------------------------------------------*/ 41 | 42 | #define OS_TYPE 0 /* 0:Win32, 1:uITRON4.0, 2:uC/OS-II, 3:FreeRTOS, 4:CMSIS-RTOS */ 43 | 44 | 45 | #if OS_TYPE == 0 /* Win32 */ 46 | #include 47 | static HANDLE Mutex[FF_VOLUMES + 1]; /* Table of mutex handle */ 48 | 49 | #elif OS_TYPE == 1 /* uITRON */ 50 | #include "itron.h" 51 | #include "kernel.h" 52 | static mtxid Mutex[FF_VOLUMES + 1]; /* Table of mutex ID */ 53 | 54 | #elif OS_TYPE == 2 /* uc/OS-II */ 55 | #include "includes.h" 56 | static OS_EVENT *Mutex[FF_VOLUMES + 1]; /* Table of mutex pinter */ 57 | 58 | #elif OS_TYPE == 3 /* FreeRTOS */ 59 | #include "FreeRTOS.h" 60 | #include "semphr.h" 61 | static SemaphoreHandle_t Mutex[FF_VOLUMES + 1]; /* Table of mutex handle */ 62 | 63 | #elif OS_TYPE == 4 /* CMSIS-RTOS */ 64 | #include "cmsis_os.h" 65 | static osMutexId Mutex[FF_VOLUMES + 1]; /* Table of mutex ID */ 66 | 67 | #endif 68 | 69 | 70 | 71 | /*------------------------------------------------------------------------*/ 72 | /* Create a Mutex */ 73 | /*------------------------------------------------------------------------*/ 74 | /* This function is called in f_mount function to create a new mutex 75 | / or semaphore for the volume. When a 0 is returned, the f_mount function 76 | / fails with FR_INT_ERR. 77 | */ 78 | 79 | int ff_mutex_create ( /* Returns 1:Function succeeded or 0:Could not create the mutex */ 80 | int vol /* Mutex ID: Volume mutex (0 to FF_VOLUMES - 1) or system mutex (FF_VOLUMES) */ 81 | ) 82 | { 83 | #if OS_TYPE == 0 /* Win32 */ 84 | Mutex[vol] = CreateMutex(NULL, FALSE, NULL); 85 | return (int)(Mutex[vol] != INVALID_HANDLE_VALUE); 86 | 87 | #elif OS_TYPE == 1 /* uITRON */ 88 | T_CMTX cmtx = {TA_TPRI,1}; 89 | 90 | Mutex[vol] = acre_mtx(&cmtx); 91 | return (int)(Mutex[vol] > 0); 92 | 93 | #elif OS_TYPE == 2 /* uC/OS-II */ 94 | OS_ERR err; 95 | 96 | Mutex[vol] = OSMutexCreate(0, &err); 97 | return (int)(err == OS_NO_ERR); 98 | 99 | #elif OS_TYPE == 3 /* FreeRTOS */ 100 | Mutex[vol] = xSemaphoreCreateMutex(); 101 | return (int)(Mutex[vol] != NULL); 102 | 103 | #elif OS_TYPE == 4 /* CMSIS-RTOS */ 104 | osMutexDef(cmsis_os_mutex); 105 | 106 | Mutex[vol] = osMutexCreate(osMutex(cmsis_os_mutex)); 107 | return (int)(Mutex[vol] != NULL); 108 | 109 | #endif 110 | } 111 | 112 | 113 | /*------------------------------------------------------------------------*/ 114 | /* Delete a Mutex */ 115 | /*------------------------------------------------------------------------*/ 116 | /* This function is called in f_mount function to delete a mutex or 117 | / semaphore of the volume created with ff_mutex_create function. 118 | */ 119 | 120 | void ff_mutex_delete ( /* Returns 1:Function succeeded or 0:Could not delete due to an error */ 121 | int vol /* Mutex ID: Volume mutex (0 to FF_VOLUMES - 1) or system mutex (FF_VOLUMES) */ 122 | ) 123 | { 124 | #if OS_TYPE == 0 /* Win32 */ 125 | CloseHandle(Mutex[vol]); 126 | 127 | #elif OS_TYPE == 1 /* uITRON */ 128 | del_mtx(Mutex[vol]); 129 | 130 | #elif OS_TYPE == 2 /* uC/OS-II */ 131 | OS_ERR err; 132 | 133 | OSMutexDel(Mutex[vol], OS_DEL_ALWAYS, &err); 134 | 135 | #elif OS_TYPE == 3 /* FreeRTOS */ 136 | vSemaphoreDelete(Mutex[vol]); 137 | 138 | #elif OS_TYPE == 4 /* CMSIS-RTOS */ 139 | osMutexDelete(Mutex[vol]); 140 | 141 | #endif 142 | } 143 | 144 | 145 | /*------------------------------------------------------------------------*/ 146 | /* Request a Grant to Access the Volume */ 147 | /*------------------------------------------------------------------------*/ 148 | /* This function is called on enter file functions to lock the volume. 149 | / When a 0 is returned, the file function fails with FR_TIMEOUT. 150 | */ 151 | 152 | int ff_mutex_take ( /* Returns 1:Succeeded or 0:Timeout */ 153 | int vol /* Mutex ID: Volume mutex (0 to FF_VOLUMES - 1) or system mutex (FF_VOLUMES) */ 154 | ) 155 | { 156 | #if OS_TYPE == 0 /* Win32 */ 157 | return (int)(WaitForSingleObject(Mutex[vol], FF_FS_TIMEOUT) == WAIT_OBJECT_0); 158 | 159 | #elif OS_TYPE == 1 /* uITRON */ 160 | return (int)(tloc_mtx(Mutex[vol], FF_FS_TIMEOUT) == E_OK); 161 | 162 | #elif OS_TYPE == 2 /* uC/OS-II */ 163 | OS_ERR err; 164 | 165 | OSMutexPend(Mutex[vol], FF_FS_TIMEOUT, &err)); 166 | return (int)(err == OS_NO_ERR); 167 | 168 | #elif OS_TYPE == 3 /* FreeRTOS */ 169 | return (int)(xSemaphoreTake(Mutex[vol], FF_FS_TIMEOUT) == pdTRUE); 170 | 171 | #elif OS_TYPE == 4 /* CMSIS-RTOS */ 172 | return (int)(osMutexWait(Mutex[vol], FF_FS_TIMEOUT) == osOK); 173 | 174 | #endif 175 | } 176 | 177 | 178 | 179 | /*------------------------------------------------------------------------*/ 180 | /* Release a Grant to Access the Volume */ 181 | /*------------------------------------------------------------------------*/ 182 | /* This function is called on leave file functions to unlock the volume. 183 | */ 184 | 185 | void ff_mutex_give ( 186 | int vol /* Mutex ID: Volume mutex (0 to FF_VOLUMES - 1) or system mutex (FF_VOLUMES) */ 187 | ) 188 | { 189 | #if OS_TYPE == 0 /* Win32 */ 190 | ReleaseMutex(Mutex[vol]); 191 | 192 | #elif OS_TYPE == 1 /* uITRON */ 193 | unl_mtx(Mutex[vol]); 194 | 195 | #elif OS_TYPE == 2 /* uC/OS-II */ 196 | OSMutexPost(Mutex[vol]); 197 | 198 | #elif OS_TYPE == 3 /* FreeRTOS */ 199 | xSemaphoreGive(Mutex[vol]); 200 | 201 | #elif OS_TYPE == 4 /* CMSIS-RTOS */ 202 | osMutexRelease(Mutex[vol]); 203 | 204 | #endif 205 | } 206 | 207 | #endif /* FF_FS_REENTRANT */ 208 | 209 | -------------------------------------------------------------------------------- /samples/pico_ws19804/pico_nes_ws19804.cpp: -------------------------------------------------------------------------------- 1 | #include "hardware/gpio.h" 2 | #include "hardware/clocks.h" 3 | #include "hardware/vreg.h" 4 | #include "pico/stdlib.h" 5 | #include "pico/multicore.h" 6 | #include "pico/util/queue.h" 7 | #include "pico/time.h" 8 | 9 | #include "ws19804.hpp" 10 | 11 | #include "pwm_audio.hpp" 12 | 13 | #include "shapones/shapones.hpp" 14 | 15 | #include "common.hpp" 16 | #include "boot_menu.hpp" 17 | 18 | // monitor pin for debugging 19 | static constexpr int PIN_MONITOR = 1; 20 | 21 | // speaker PWM out 22 | static constexpr int PIN_SPEAKER = 28; 23 | 24 | // APU configuration 25 | static constexpr int SPK_LATENCY = 256; 26 | static constexpr int SPK_PWM_FREQ = 22050; 27 | 28 | // NES color table 29 | static const uint16_t COLOR_TABLE[] = { 30 | 0x555, 0x027, 0x019, 0x308, 0x406, 0x603, 0x500, 0x410, 31 | 0x230, 0x040, 0x040, 0x040, 0x034, 0x000, 0x000, 0x000, 32 | 0x999, 0x05C, 0x33F, 0x62E, 0x81B, 0xA16, 0x922, 0x740, 33 | 0x560, 0x270, 0x080, 0x072, 0x067, 0x000, 0x000, 0x000, 34 | 0xFFF, 0x5AF, 0x78F, 0xB6F, 0xE5F, 0xF5B, 0xF76, 0xD82, 35 | 0xAB0, 0x7C0, 0x5D2, 0x3D7, 0x3BD, 0x444, 0x000, 0x000, 36 | 0xFFF, 0xADF, 0xCCF, 0xDBF, 0xFBF, 0xFBD, 0xFBB, 0xEC9, 37 | 0xDD7, 0xBE7, 0xAE9, 0x9EB, 0xADE, 0xAAA, 0x000, 0x000, 38 | }; 39 | 40 | // line buffer FIFO between core1 --> core0 41 | // <------------- STRIDE ------------> 42 | // +---------+-----------------------+ A 43 | // | y coord | color numbers (256px) | | 44 | // | 1 Byte | 256 Byte | | 45 | // +---------+-----------------------+ DEPTH 46 | // | : | : | | 47 | // | : | : | | 48 | // +---------+-----------------------+ V 49 | static constexpr int LINE_FIFO_DEPTH = 8; 50 | static constexpr int LINE_FIFO_STRIDE = nes::SCREEN_WIDTH + 1; 51 | static uint8_t line_buff[LINE_FIFO_DEPTH * LINE_FIFO_STRIDE]; 52 | static volatile int line_fifo_wptr = 0; 53 | static volatile int line_fifo_rptr = 0; 54 | 55 | // sound buffer for DMA 56 | static uint8_t spk_buff[SPK_LATENCY]; 57 | 58 | // start game 59 | static void boot_nes(); 60 | 61 | // core0 main loop 62 | static void cpu_loop(); 63 | 64 | // core1 main loop 65 | static void ppu_loop(); 66 | 67 | // APU DMA finish IRQ handler 68 | static void apu_dma_handler(); 69 | 70 | // let APU fill the sound buffer 71 | static void apu_fill_buffer(PwmAudio::sample_t *buff); 72 | 73 | // PWM audio driver 74 | PwmAudio speaker(PIN_SPEAKER, SPK_LATENCY, 8, (float)SYS_CLK_FREQ / SPK_PWM_FREQ, apu_dma_handler); 75 | 76 | int main() { 77 | // setup clocks 78 | vreg_set_voltage(VREG_VOLTAGE_1_30); 79 | sleep_ms(100); 80 | stdio_init_all(); 81 | set_sys_clock_khz(SYS_CLK_FREQ / 1000, true); 82 | setup_default_uart(); 83 | 84 | // setup monitor pin 85 | gpio_init(PIN_MONITOR); 86 | gpio_set_dir(PIN_MONITOR, GPIO_OUT); 87 | gpio_put(PIN_MONITOR, 0); 88 | 89 | // setup input pins 90 | for(int i = 0; i < 8; i++) { 91 | int pin = input_pins[i]; 92 | gpio_init(pin); 93 | gpio_set_dir(pin, GPIO_IN); 94 | gpio_pull_up(pin); 95 | } 96 | 97 | // setup WAVESHARE-19804 98 | ws19804::init(SYS_CLK_FREQ); 99 | 100 | // show boot menu 101 | if ( ! boot_menu()) { 102 | for(;;) sleep_ms(100); 103 | } 104 | 105 | // boot game 106 | boot_nes(); 107 | 108 | return 0; 109 | } 110 | 111 | static void boot_nes() { 112 | // set APU sampling rate 113 | nes::apu::set_sampling_rate(SPK_PWM_FREQ); 114 | 115 | // reset 116 | nes::reset(); 117 | 118 | // start APU loop 119 | apu_fill_buffer(speaker.get_buffer(0)); 120 | apu_fill_buffer(speaker.get_buffer(1)); 121 | speaker.play(); 122 | 123 | // start PPU loop 124 | multicore_launch_core1(ppu_loop); 125 | 126 | // start CPU loop 127 | cpu_loop(); 128 | } 129 | 130 | static void cpu_loop() { 131 | auto t_last_frame = get_absolute_time(); 132 | int frame_count = 0; 133 | char fps_str[16]; 134 | for(;;) { 135 | // run CPU 136 | nes::cpu::service(); 137 | 138 | // update input status 139 | nes::input::InputStatus input_status; 140 | input_status.raw = 0; 141 | for(int i = 0; i < 8; i++) { 142 | if ( ! gpio_get(input_pins[i])) { 143 | input_status.raw |= (1 << i); 144 | } 145 | } 146 | nes::input::set_raw(0, input_status); 147 | 148 | // check line buffer FIFO state 149 | int fifo_rptr = line_fifo_rptr; 150 | if (line_fifo_wptr != fifo_rptr) { 151 | // convert color number to RGB444 152 | int y = line_buff[fifo_rptr * LINE_FIFO_STRIDE]; 153 | int x0 = (nes::SCREEN_WIDTH - FRAME_BUFF_WIDTH) / 2; 154 | uint8_t *rd_ptr = line_buff + (fifo_rptr * LINE_FIFO_STRIDE + x0 + 1); 155 | uint8_t *wr_ptr = frame_buff + (y * FRAME_BUFF_STRIDE); 156 | for (int x = 0; x < FRAME_BUFF_WIDTH; x+=2) { 157 | uint16_t c0 = COLOR_TABLE[*(rd_ptr++) & 0x3f]; 158 | uint16_t c1 = COLOR_TABLE[*(rd_ptr++) & 0x3f]; 159 | *(wr_ptr++) = (c0 >> 4) & 0xff; 160 | *(wr_ptr++) = ((c0 << 4) & 0xf0) | (c1 >> 8) & 0xf; 161 | *(wr_ptr++) = c1 & 0xff; 162 | } 163 | line_fifo_rptr = (fifo_rptr + 1) % LINE_FIFO_DEPTH; 164 | 165 | if (y == nes::SCREEN_HEIGHT - 1) { 166 | // fps measurement 167 | auto t_now = get_absolute_time(); 168 | if (frame_count < 60-1) { 169 | frame_count++; 170 | } 171 | else { 172 | auto t_diff = absolute_time_diff_us(t_last_frame, t_now); 173 | float fps = (60.0f * 1000000) / t_diff; 174 | sprintf(fps_str, "%5.2ffps", fps); 175 | t_last_frame = t_now; 176 | frame_count = 0; 177 | } 178 | 179 | // DMA transfer 180 | ws19804::finish_write_data(); 181 | draw_string(0, 0, fps_str); 182 | ws19804::start_write_data(25, 0, FRAME_BUFF_WIDTH, FRAME_BUFF_HEIGHT, frame_buff); 183 | static int tmp = 0; 184 | gpio_put(PIN_MONITOR, tmp); 185 | tmp ^= 1; 186 | } 187 | } 188 | } 189 | } 190 | 191 | static void ppu_loop() { 192 | constexpr int FRAME_DELAY_US = 16666; 193 | absolute_time_t next_time = delayed_by_us(get_absolute_time(), FRAME_DELAY_US); 194 | 195 | for(;;) { 196 | int wptr = line_fifo_wptr; 197 | bool eol = nes::ppu::service(&line_buff[wptr * LINE_FIFO_STRIDE + 1]); 198 | int y = nes::ppu::current_focus_y(); 199 | if (eol && y < nes::SCREEN_HEIGHT) { 200 | // Vsync 201 | if (y == 0) { 202 | busy_wait_until(next_time); 203 | next_time = delayed_by_us(get_absolute_time(), FRAME_DELAY_US); 204 | } 205 | 206 | // push new line 207 | line_buff[wptr * LINE_FIFO_STRIDE] = y; 208 | line_fifo_wptr = (wptr + 1) % LINE_FIFO_DEPTH; 209 | } 210 | } 211 | } 212 | 213 | static void apu_dma_handler() { 214 | speaker.flip_buffer(); 215 | apu_fill_buffer(speaker.get_next_buffer()); 216 | } 217 | 218 | static void apu_fill_buffer(PwmAudio::sample_t *buff) { 219 | nes::apu::service(spk_buff, speaker.LATENCY); 220 | for (int i = 0; i < speaker.LATENCY; i++) { 221 | buff[i] = spk_buff[i]; 222 | } 223 | } -------------------------------------------------------------------------------- /samples/font/mono8x16/mono8x16_bmp.cpp: -------------------------------------------------------------------------------- 1 | #include "stdint.h" 2 | #include "mono8x16.hpp" 3 | 4 | namespace mono8x16 { 5 | 6 | const uint8_t bmp[] = { 7 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 8 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 9 | 0x77, 0x77, 0x66, 0x66, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 10 | 0x66, 0x66, 0x66, 0xff, 0x66, 0x66, 0x66, 0x66, 0xff, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 11 | 0x18, 0x7e, 0xdb, 0xdb, 0x1b, 0x1e, 0x78, 0xd8, 0xdb, 0xdb, 0x7e, 0x18, 0x00, 0x00, 0x00, 0x00, 12 | 0xde, 0x73, 0x73, 0x33, 0x3e, 0x18, 0x18, 0x7c, 0xcc, 0xce, 0xce, 0x7b, 0x00, 0x00, 0x00, 0x00, 13 | 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x1c, 0x1e, 0xb3, 0xf3, 0x63, 0x73, 0xde, 0x00, 0x00, 0x00, 0x00, 14 | 0x1c, 0x1c, 0x18, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 15 | 0x30, 0x18, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 16 | 0x0c, 0x18, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 17 | 0x00, 0x00, 0x18, 0x99, 0xdb, 0x7e, 0x3c, 0x7e, 0xdb, 0x99, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 18 | 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 19 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x1c, 0x1c, 0x18, 0x18, 0x0c, 0x00, 20 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 21 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x1c, 0x1c, 0x00, 0x00, 0x00, 0x00, 22 | 0xc0, 0x60, 0x60, 0x30, 0x30, 0x18, 0x18, 0x0c, 0x0c, 0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 23 | 0x3c, 0x66, 0xc3, 0xc3, 0xe3, 0xd3, 0xcb, 0xc7, 0xc3, 0xc3, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 24 | 0x18, 0x1c, 0x1e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 25 | 0x3c, 0x66, 0xc3, 0xc3, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x03, 0xff, 0x00, 0x00, 0x00, 0x00, 26 | 0x3c, 0x66, 0xc3, 0xc3, 0x60, 0x38, 0x60, 0xc0, 0xc3, 0xc3, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 27 | 0x0c, 0x0c, 0x0c, 0x06, 0x36, 0x36, 0x33, 0x33, 0xff, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 28 | 0x7e, 0x06, 0x06, 0x03, 0x3f, 0x63, 0xc0, 0xc0, 0xc3, 0xc3, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 29 | 0x18, 0x0c, 0x0c, 0x06, 0x3e, 0x67, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 30 | 0xff, 0xc3, 0xc3, 0x63, 0x60, 0x30, 0x30, 0x30, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 31 | 0x3c, 0x66, 0xc3, 0xc3, 0x66, 0x3c, 0x66, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 32 | 0x3c, 0x66, 0xc3, 0xc3, 0xc3, 0xc3, 0xe6, 0x7c, 0x60, 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 33 | 0x00, 0x00, 0x00, 0x1c, 0x1c, 0x1c, 0x00, 0x00, 0x00, 0x1c, 0x1c, 0x1c, 0x00, 0x00, 0x00, 0x00, 34 | 0x00, 0x00, 0x00, 0x1c, 0x1c, 0x1c, 0x00, 0x00, 0x00, 0x1c, 0x1c, 0x1c, 0x18, 0x18, 0x0c, 0x00, 35 | 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 36 | 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 37 | 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 38 | 0x3c, 0x66, 0xc3, 0xc3, 0x60, 0x30, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 39 | 0x3c, 0x66, 0xc3, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0x7b, 0x03, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 40 | 0x18, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0x66, 0x66, 0xff, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, 41 | 0x3f, 0x63, 0xc3, 0xc3, 0x63, 0x3f, 0x63, 0xc3, 0xc3, 0xc3, 0x63, 0x3f, 0x00, 0x00, 0x00, 0x00, 42 | 0x3c, 0x66, 0xc3, 0xc3, 0x03, 0x03, 0x03, 0x03, 0xc3, 0xc3, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 43 | 0x3f, 0x63, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x63, 0x3f, 0x00, 0x00, 0x00, 0x00, 44 | 0xff, 0x03, 0x03, 0x03, 0x03, 0x7f, 0x03, 0x03, 0x03, 0x03, 0x03, 0xff, 0x00, 0x00, 0x00, 0x00, 45 | 0xff, 0x03, 0x03, 0x03, 0x03, 0x7f, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 46 | 0x3c, 0x66, 0xc3, 0x03, 0x03, 0xf3, 0xc3, 0xc3, 0xc3, 0xc3, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00, 47 | 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xff, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, 48 | 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 49 | 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 50 | 0xc3, 0x63, 0x33, 0x33, 0x1b, 0x0f, 0x0f, 0x1b, 0x33, 0x33, 0x63, 0xc3, 0x00, 0x00, 0x00, 0x00, 51 | 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xff, 0x00, 0x00, 0x00, 0x00, 52 | 0xc3, 0xc3, 0xe7, 0xe7, 0xff, 0xff, 0xff, 0xdb, 0xdb, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, 53 | 0xc3, 0xc7, 0xc7, 0xcf, 0xcf, 0xdb, 0xdb, 0xf3, 0xf3, 0xe3, 0xe3, 0xc3, 0x00, 0x00, 0x00, 0x00, 54 | 0x3c, 0x66, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 55 | 0x3f, 0x63, 0xc3, 0xc3, 0xc3, 0xc3, 0x63, 0x3f, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 56 | 0x3c, 0x66, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xf3, 0xf3, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00, 57 | 0x3f, 0x63, 0xc3, 0xc3, 0xc3, 0x63, 0x3f, 0x33, 0x63, 0x63, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, 58 | 0x3c, 0x66, 0xc3, 0xc3, 0x06, 0x1c, 0x30, 0x60, 0xc3, 0xc3, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 59 | 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 60 | 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 61 | 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x66, 0x66, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 62 | 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xdb, 0xff, 0x7e, 0x7e, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 63 | 0xc3, 0x66, 0x66, 0x3c, 0x3c, 0x18, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00, 64 | 0xc3, 0xc3, 0x66, 0x66, 0x66, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 65 | 0xff, 0x60, 0x60, 0x30, 0x30, 0x18, 0x18, 0x0c, 0x0c, 0x06, 0x06, 0xff, 0x00, 0x00, 0x00, 0x00, 66 | 0x7c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x7c, 0x00, 0x00, 0x00, 0x00, 67 | 0x03, 0x06, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0x30, 0x30, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 68 | 0x3e, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3e, 0x00, 0x00, 0x00, 0x00, 69 | 0x18, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 70 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 71 | 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 72 | 0x00, 0x00, 0x00, 0x3c, 0x62, 0xc0, 0xfc, 0xc6, 0xc3, 0xc3, 0xe6, 0xdc, 0x00, 0x00, 0x00, 0x00, 73 | 0x03, 0x03, 0x03, 0x3b, 0x67, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x67, 0x3b, 0x00, 0x00, 0x00, 0x00, 74 | 0x00, 0x00, 0x00, 0x3c, 0x66, 0xc3, 0x03, 0x03, 0x03, 0xc3, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 75 | 0xc0, 0xc0, 0xc0, 0xdc, 0xe6, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xe6, 0xdc, 0x00, 0x00, 0x00, 0x00, 76 | 0x00, 0x00, 0x00, 0x3c, 0x66, 0xc3, 0xc3, 0xff, 0x03, 0x03, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 77 | 0x78, 0xcc, 0xcc, 0x0c, 0x0c, 0xff, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 78 | 0x00, 0x00, 0x00, 0xdc, 0xe6, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xe6, 0xdc, 0xc0, 0xc0, 0x63, 0x3e, 79 | 0x03, 0x03, 0x03, 0x3b, 0x67, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, 80 | 0x18, 0x18, 0x00, 0x00, 0x1c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 81 | 0x60, 0x60, 0x00, 0x00, 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x33, 0x1e, 82 | 0x03, 0x03, 0x03, 0xc3, 0x63, 0x33, 0x1b, 0x0f, 0x1f, 0x33, 0x63, 0xc3, 0x00, 0x00, 0x00, 0x00, 83 | 0x1c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 84 | 0x00, 0x00, 0x00, 0x4b, 0xff, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, 0x00, 85 | 0x00, 0x00, 0x00, 0x3b, 0x67, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, 86 | 0x00, 0x00, 0x00, 0x3c, 0x66, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 87 | 0x00, 0x00, 0x00, 0x3b, 0x67, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x67, 0x3b, 0x03, 0x03, 0x03, 0x03, 88 | 0x00, 0x00, 0x00, 0xdc, 0xe6, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xe6, 0xdc, 0xc0, 0xc0, 0xc0, 0xc0, 89 | 0x00, 0x00, 0x00, 0x3b, 0x67, 0xc3, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 90 | 0x00, 0x00, 0x00, 0x7e, 0xc3, 0xc3, 0x0e, 0x38, 0x60, 0xc3, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00, 91 | 0x0c, 0x0c, 0x0c, 0xff, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, 92 | 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xe6, 0xdc, 0x00, 0x00, 0x00, 0x00, 93 | 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0x66, 0x66, 0x3c, 0x3c, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 94 | 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xdb, 0xdb, 0xdb, 0x7e, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 95 | 0x00, 0x00, 0x00, 0xc3, 0x66, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00, 96 | 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0x66, 0x66, 0x66, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x0c, 0x07, 97 | 0x00, 0x00, 0x00, 0xff, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0xff, 0x00, 0x00, 0x00, 0x00, 98 | 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00, 99 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 100 | 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00, 101 | 0x8e, 0xdb, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 102 | 0xff, 0x81, 0xc3, 0xa5, 0xa5, 0x99, 0x99, 0xa5, 0xa5, 0xc3, 0x81, 0xff, 0x00, 0x00, 0x00, 0x00, 103 | }; 104 | 105 | } 106 | -------------------------------------------------------------------------------- /samples/pico_ws19804/ws19804.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "ws19804.hpp" 3 | #include "ws19804_c.h" 4 | #include "ws19804.pio.h" 5 | 6 | namespace ws19804 { 7 | 8 | static int sys_clock_hz; 9 | static direction_t curr_dir = EMPTY; 10 | static int curr_speed = 10 * MHZ; 11 | 12 | static PIO spi_pio; 13 | static uint spi_sm; 14 | static uint spi_offset; 15 | static uint dma_tx; 16 | 17 | // SPI push byte 18 | static void pio_push(uint8_t data); 19 | 20 | // SPI pop byte 21 | static uint8_t pio_pop(); 22 | 23 | // wait for SPI to idle 24 | static void pio_wait_idle(); 25 | 26 | // setup SPI direction and speed 27 | static void setup_pio(direction_t new_dir, int new_speed); 28 | 29 | // set SPI direction 30 | static void set_spi_direction(direction_t new_dir); 31 | 32 | void init(int sys_clk_hz) { 33 | sys_clock_hz = sys_clk_hz; 34 | 35 | spi_pio = pio0; 36 | spi_sm = 0; 37 | 38 | spi_offset = pio_add_program(spi_pio, &ws19804_program); 39 | pio_gpio_init(spi_pio, PIN_MISO); 40 | pio_gpio_init(spi_pio, PIN_MOSI); 41 | pio_gpio_init(spi_pio, PIN_SCK); 42 | pio_sm_set_consecutive_pindirs(spi_pio, spi_sm, PIN_MISO, 1, false); 43 | pio_sm_set_consecutive_pindirs(spi_pio, spi_sm, PIN_MOSI, 1, true); 44 | pio_sm_set_consecutive_pindirs(spi_pio, spi_sm, PIN_SCK, 1, true); 45 | 46 | dma_tx = dma_claim_unused_channel(true); 47 | 48 | gpio_init(PIN_RST); 49 | gpio_init(PIN_DC); 50 | gpio_init(PIN_BL); 51 | gpio_init(PIN_LCD_CS); 52 | gpio_init(PIN_TP_CS); 53 | gpio_set_dir(PIN_RST, GPIO_OUT); 54 | gpio_set_dir(PIN_DC, GPIO_OUT); 55 | gpio_set_dir(PIN_BL, GPIO_OUT); 56 | gpio_set_dir(PIN_LCD_CS, GPIO_OUT); 57 | gpio_set_dir(PIN_TP_CS, GPIO_OUT); 58 | gpio_put(PIN_RST, 1); 59 | gpio_put(PIN_LCD_CS, 1); 60 | gpio_put(PIN_TP_CS, 1); 61 | gpio_put(PIN_BL, 0); 62 | 63 | // hardware reset 64 | gpio_put(PIN_RST, 1); 65 | sleep_ms(1); 66 | gpio_put(PIN_RST, 0); 67 | sleep_ms(10); 68 | gpio_put(PIN_RST, 1); 69 | sleep_ms(10); 70 | 71 | write_command(0x11); 72 | sleep_ms(120); 73 | 74 | //MX, MY, RGB mode 75 | { 76 | uint8_t data[] = { 0xa0 }; 77 | write_command(0x36, data, sizeof(data)); 78 | } 79 | 80 | { 81 | //uint8_t data[] = { 0x05 }; // RGB565 82 | uint8_t data[] = { 0x03 }; // RGB444 83 | write_command(0x3a, data, sizeof(data)); 84 | } 85 | 86 | { 87 | uint8_t data[] = { 0x0c, 0x0c, 0x00, 0x33, 0x33 }; 88 | write_command(0xb2, data, sizeof(data)); 89 | } 90 | 91 | { 92 | uint8_t data[] = { 0x72 }; 93 | write_command(0xb7, data, sizeof(data)); 94 | } 95 | 96 | { 97 | uint8_t data[] = { 0x3d }; 98 | write_command(0xbb, data, sizeof(data)); 99 | } 100 | 101 | { 102 | uint8_t data[] = { 0x01 }; 103 | write_command(0xc2, data, sizeof(data)); 104 | } 105 | 106 | { 107 | uint8_t data[] = { 0x19 }; 108 | write_command(0xc3, data, sizeof(data)); 109 | } 110 | 111 | { 112 | uint8_t data[] = { 0x20 }; 113 | write_command(0xc4, data, sizeof(data)); 114 | } 115 | 116 | { 117 | uint8_t data[] = { 0x0f }; 118 | write_command(0xc6, data, sizeof(data)); 119 | } 120 | 121 | { 122 | uint8_t data[] = { 0xa4, 0xa1 }; 123 | write_command(0xd0, data, sizeof(data)); 124 | } 125 | 126 | { 127 | uint8_t data[] = { 128 | 0x70, 0x04, 0x08, 0x09, 0x09, 0x05, 0x2a, 0x33, 129 | 0x41, 0x07, 0x13, 0x13, 0x29, 0x2f, 130 | }; 131 | write_command(0xe0, data, sizeof(data)); 132 | } 133 | { 134 | uint8_t data[] = { 135 | 0x70, 0x03, 0x09, 0x0a, 0x09, 0x06, 0x2b, 0x34, 136 | 0x41, 0x07, 0x12, 0x14, 0x28, 0x2e, 137 | }; 138 | write_command(0xe1, data, sizeof(data)); 139 | } 140 | 141 | write_command(0x21); 142 | 143 | clear(0); 144 | 145 | write_command(0x29); 146 | 147 | gpio_put(PIN_BL, 1); 148 | } 149 | 150 | void setup_pio(direction_t new_dir, int new_speed) { 151 | if (new_dir == curr_dir && new_speed == curr_speed) return; 152 | 153 | float div = sys_clock_hz / 2.f / new_speed; 154 | 155 | pio_sm_set_enabled(spi_pio, spi_sm, false); 156 | 157 | // load new PIO 158 | if (new_dir == TX) { 159 | pio_sm_config pio_cfg = ws19804_program_get_default_config(spi_offset); 160 | sm_config_set_out_pins(&pio_cfg, PIN_MOSI, 1); 161 | sm_config_set_sideset_pins(&pio_cfg, PIN_SCK); 162 | sm_config_set_fifo_join(&pio_cfg, PIO_FIFO_JOIN_TX); 163 | sm_config_set_out_shift(&pio_cfg, false, true, 8); 164 | sm_config_set_clkdiv(&pio_cfg, div); 165 | pio_sm_init(spi_pio, spi_sm, spi_offset, &pio_cfg); 166 | } 167 | else if (new_dir == RX) { 168 | pio_sm_config pio_cfg = ws19804_program_get_default_config(spi_offset); 169 | sm_config_set_out_pins(&pio_cfg, PIN_MOSI, 1); 170 | sm_config_set_in_pins(&pio_cfg, PIN_MISO); 171 | sm_config_set_sideset_pins(&pio_cfg, PIN_SCK); 172 | sm_config_set_out_shift(&pio_cfg, false, true, 8); 173 | sm_config_set_in_shift(&pio_cfg, false, true, 8); 174 | sm_config_set_clkdiv(&pio_cfg, div); 175 | pio_sm_init(spi_pio, spi_sm, spi_offset, &pio_cfg); 176 | } 177 | 178 | hw_set_bits(&spi_pio->input_sync_bypass, 1u << PIN_MISO); 179 | pio_sm_set_enabled(spi_pio, spi_sm, true); 180 | 181 | curr_dir = new_dir; 182 | curr_speed = new_speed; 183 | } 184 | 185 | void set_spi_direction(direction_t new_dir) { 186 | setup_pio(new_dir, curr_speed); 187 | } 188 | 189 | void set_spi_speed(int new_speed) { 190 | setup_pio(curr_dir, new_speed); 191 | } 192 | 193 | void clear(uint16_t color) { 194 | uint8_t data[WIDTH * 3 / 2]; 195 | for (int x = 0; x < WIDTH * 3 / 2; x++) { 196 | data[x] = color; 197 | } 198 | for (int y = 0; y < HEIGHT; y++) { 199 | start_write_data(0, y, WIDTH, 1, data); 200 | finish_write_data(); 201 | } 202 | } 203 | 204 | void start_write_data(int x0, int y0, int w, int h, uint8_t *data) { 205 | int x1 = x0 + w - 1; 206 | int y1 = y0 + h - 1; 207 | { 208 | uint8_t data[] = { 209 | (uint8_t)(x0 >> 8), 210 | (uint8_t)(x0 & 0xff), 211 | (uint8_t)(x1 >> 8), 212 | (uint8_t)(x1 & 0xff) 213 | }; 214 | write_command(0x2a, data, sizeof(data)); 215 | } 216 | { 217 | uint8_t data[] = { 218 | (uint8_t)(y0 >> 8), 219 | (uint8_t)(y0 & 0xff), 220 | (uint8_t)(y1 >> 8), 221 | (uint8_t)(y1 & 0xff) 222 | }; 223 | write_command(0x2b, data, sizeof(data)); 224 | } 225 | //write_command(0x2c, data, w * h * 3 / 2); 226 | { 227 | gpio_put(PIN_DC, 0); 228 | gpio_put(PIN_LCD_CS, 0); 229 | uint8_t cmd = 0x2c; 230 | write_blocking(&cmd, 1); 231 | gpio_put(PIN_DC, 1); 232 | 233 | { 234 | dma_channel_config dma_cfg = dma_channel_get_default_config(dma_tx); 235 | channel_config_set_transfer_data_size(&dma_cfg, DMA_SIZE_8); 236 | channel_config_set_dreq(&dma_cfg, pio_get_dreq(spi_pio, spi_sm, true)); 237 | dma_channel_configure(dma_tx, &dma_cfg, 238 | &spi_pio->txf[spi_sm], // write address 239 | data, // read address 240 | w * h * 3 / 2, // element count (each element is of size transfer_data_size) 241 | false); // don't start yet 242 | } 243 | 244 | dma_start_channel_mask(1u << dma_tx); 245 | } 246 | } 247 | 248 | void finish_write_data() { 249 | dma_channel_wait_for_finish_blocking(dma_tx); 250 | pio_wait_idle(); 251 | gpio_put(PIN_LCD_CS, 1); 252 | } 253 | 254 | void write_command(uint8_t cmd, const uint8_t *data, int len) { 255 | set_spi_direction(TX); 256 | gpio_put(PIN_DC, 0); // command mode 257 | gpio_put(PIN_LCD_CS, 0); 258 | write_blocking(&cmd, 1); 259 | if (data) { 260 | gpio_put(PIN_DC, 1); // data mode 261 | write_blocking(data, len); 262 | } 263 | gpio_put(PIN_LCD_CS, 1); 264 | } 265 | 266 | void write_command(uint8_t cmd) { 267 | set_spi_direction(TX); 268 | write_command(cmd, nullptr, 0); 269 | } 270 | 271 | void write_blocking(const uint8_t *data, int len) { 272 | set_spi_direction(TX); 273 | for(int i = 0; i < len; i++) { 274 | pio_push(data[i]); 275 | } 276 | pio_wait_idle(); 277 | } 278 | 279 | void read_blocking(uint8_t tx_repeat, uint8_t *buff, int len) { 280 | set_spi_direction(RX); 281 | int tx_remain = len, rx_remain = len; 282 | io_rw_8 *txfifo = (io_rw_8 *) &spi_pio->txf[spi_sm]; 283 | io_rw_8 *rxfifo = (io_rw_8 *) &spi_pio->rxf[spi_sm]; 284 | while (tx_remain || rx_remain) { 285 | if (tx_remain && !pio_sm_is_tx_fifo_full(spi_pio, spi_sm)) { 286 | *txfifo = tx_repeat; 287 | --tx_remain; 288 | } 289 | if (rx_remain && !pio_sm_is_rx_fifo_empty(spi_pio, spi_sm)) { 290 | *buff++ = *rxfifo; 291 | --rx_remain; 292 | } 293 | } 294 | } 295 | 296 | static void pio_push(uint8_t data) { 297 | while (pio_sm_is_tx_fifo_full(spi_pio, spi_sm)) { } 298 | *(volatile uint8_t*)&spi_pio->txf[spi_sm] = data; 299 | } 300 | 301 | static uint8_t pio_pop() { 302 | while (pio_sm_is_rx_fifo_empty(spi_pio, spi_sm)) { } 303 | return *(volatile uint8_t*)&spi_pio->rxf[spi_sm]; 304 | } 305 | 306 | static void pio_wait_idle() { 307 | uint32_t stall_mask = 1u << (spi_sm + PIO_FDEBUG_TXSTALL_LSB); 308 | spi_pio->fdebug = stall_mask; 309 | while (!(spi_pio->fdebug & stall_mask)) { } 310 | } 311 | 312 | extern "C" { 313 | 314 | void ws19804_write_blocking(const uint8_t *buff, int len) { 315 | ws19804::write_blocking(buff, len); 316 | } 317 | void ws19804_read_blocking(uint8_t tx_repeat, uint8_t *buff, int len) { 318 | ws19804::read_blocking(tx_repeat, buff, len); 319 | } 320 | 321 | } 322 | 323 | } 324 | -------------------------------------------------------------------------------- /core/src/apu.cpp: -------------------------------------------------------------------------------- 1 | #include "shapones/shapones.hpp" 2 | 3 | namespace nes::apu { 4 | 5 | static constexpr int QUARTER_FRAME_FREQUENCY = 240; 6 | static constexpr int QUARTER_FRAME_PHASE_PREC = 16; 7 | static constexpr uint32_t QUARTER_FRAME_PHASE_PERIOD = 1ul << QUARTER_FRAME_PHASE_PREC; 8 | 9 | static int sampling_rate; 10 | static uint32_t pulse_timer_step; 11 | static uint32_t triangle_timer_step; 12 | static int qframe_phase_step; 13 | 14 | static int quarter_frame_phase; 15 | static int quarter_frame_count; 16 | static bool frame_step; 17 | static bool half_frame_step; 18 | static bool quarter_frame_step; 19 | 20 | // see: https://www.nesdev.org/wiki/APU_Length_Counter 21 | static uint8_t LENGTH_TABLE[] = { 22 | 10,254, 20, 2, 40, 4, 80, 6, 160, 8, 60, 10, 14, 12, 26, 14, 23 | 12, 16, 24, 18, 48, 20, 96, 22, 192, 24, 72, 26, 16, 28, 32, 30 24 | }; 25 | 26 | PulseState pulse[2]; 27 | TriangleState triangle; 28 | NoiseState noise; 29 | Status status; 30 | 31 | static void pulse_write_reg0(PulseState *s, uint8_t reg0); 32 | static void pulse_write_reg1(PulseState *s, uint8_t reg1); 33 | static void pulse_write_reg2(PulseState *s, uint8_t reg1); 34 | static void pulse_write_reg3(PulseState *s, uint8_t reg3); 35 | static void triangle_write_reg0(TriangleState *s, uint8_t reg0); 36 | static void triangle_write_reg2(TriangleState *s, uint8_t reg3); 37 | static void triangle_write_reg3(TriangleState *s, uint8_t reg3); 38 | static void noise_write_reg0(NoiseState *s, uint8_t reg0); 39 | static void noise_write_reg3(NoiseState *s, uint8_t reg3); 40 | static int step_envelope(Envelope *e); 41 | static int step_sweep(PulseState *s); 42 | static int sample_pulse(PulseState *s); 43 | static int sample_triangle(TriangleState *s); 44 | static int sample_noise(NoiseState *s); 45 | 46 | void reset() { 47 | for (int i = 0; i < 2; i++) { 48 | pulse[i].timer = 0; 49 | pulse[i].length = 0; 50 | pulse[i].waveform = 0; 51 | pulse[i].phase = 0; 52 | pulse[i].sweep.flags = 0; 53 | } 54 | 55 | triangle.length = 0; 56 | noise.length = 0; 57 | noise.lfsr = 1; 58 | 59 | quarter_frame_phase = 0; 60 | quarter_frame_count = 0; 61 | frame_step = false; 62 | half_frame_step = false; 63 | quarter_frame_step = false; 64 | } 65 | 66 | // todo: exclusive control 67 | uint8_t reg_read(addr_t addr) { 68 | return 0; 69 | } 70 | 71 | // todo: exclusive control 72 | void reg_write(addr_t addr, uint8_t value) { 73 | switch (addr) { 74 | case REG_PULSE1_REG0: pulse_write_reg0(&pulse[0], value); break; 75 | case REG_PULSE1_REG1: pulse_write_reg1(&pulse[0], value); break; 76 | case REG_PULSE1_REG2: pulse_write_reg2(&pulse[0], value); break; 77 | case REG_PULSE1_REG3: pulse_write_reg3(&pulse[0], value); break; 78 | case REG_PULSE2_REG0: pulse_write_reg0(&pulse[1], value); break; 79 | case REG_PULSE2_REG1: pulse_write_reg1(&pulse[1], value); break; 80 | case REG_PULSE2_REG2: pulse_write_reg2(&pulse[1], value); break; 81 | case REG_PULSE2_REG3: pulse_write_reg3(&pulse[1], value); break; 82 | case REG_TRIANGLE_REG0: triangle_write_reg0(&triangle, value); break; 83 | case REG_TRIANGLE_REG2: triangle_write_reg2(&triangle, value); break; 84 | case REG_TRIANGLE_REG3: triangle_write_reg3(&triangle, value); break; 85 | case REG_NOISE_REG0: noise_write_reg0(&noise, value); break; 86 | case REG_NOISE_REG3: noise_write_reg3(&noise, value); break; 87 | case REG_STATUS: 88 | status.raw = value; 89 | if ( ! status.pulse0_enable) pulse[0].length = 0; 90 | if ( ! status.pulse1_enable) pulse[1].length = 0; 91 | if ( ! status.triangle_enable) triangle.length = 0; 92 | if ( ! status.noise_enable) noise.length = 0; 93 | status.dmc_interrupt = 0; 94 | break; 95 | } 96 | } 97 | 98 | void set_sampling_rate(int rate_hz) { 99 | sampling_rate = rate_hz; 100 | pulse_timer_step = (1ULL << TIMER_PREC) * cpu::CLOCK_FREQUENCY / sampling_rate / 2; 101 | triangle_timer_step = (1ULL << TIMER_PREC) * cpu::CLOCK_FREQUENCY / sampling_rate; 102 | qframe_phase_step = (QUARTER_FRAME_FREQUENCY * QUARTER_FRAME_PHASE_PERIOD) / rate_hz; 103 | } 104 | 105 | void service(uint8_t *buff, int len) { 106 | for (int i = 0; i < len; i++) { 107 | quarter_frame_phase += qframe_phase_step; 108 | if (quarter_frame_phase >= QUARTER_FRAME_PHASE_PERIOD) { 109 | quarter_frame_phase -= QUARTER_FRAME_PHASE_PERIOD; 110 | 111 | frame_step = (quarter_frame_count & 3) == 3; 112 | half_frame_step = (quarter_frame_count & 1) == 1; 113 | quarter_frame_step = true; 114 | 115 | quarter_frame_count = (quarter_frame_count + 1) & 0x3; 116 | } 117 | else { 118 | frame_step = false; 119 | half_frame_step = false; 120 | quarter_frame_step = false; 121 | } 122 | 123 | buff[i] = 4 * ( 124 | sample_pulse(&pulse[0]) + 125 | sample_pulse(&pulse[1]) + 126 | sample_triangle(&triangle) + 127 | sample_noise(&noise) 128 | ); 129 | } 130 | } 131 | 132 | static void pulse_write_reg0(PulseState *s, uint8_t reg0) { 133 | // duty pattern 134 | switch ((reg0 >> 6) & 0x3) { 135 | case 0 : s->waveform = 0b00000001; break; 136 | case 1 : s->waveform = 0b00000011; break; 137 | case 2 : s->waveform = 0b00001111; break; 138 | default: s->waveform = 0b11111100; break; 139 | } 140 | 141 | s->envelope.flags = 0; 142 | if (reg0 & 0x10) s->envelope.flags |= ENV_FLAG_CONSTANT; 143 | if (reg0 & 0x20) s->envelope.flags |= ENV_FLAG_HALT_LOOP; 144 | s->envelope.volume = reg0 & 0xf; 145 | 146 | if (!(s->envelope.flags & ENV_FLAG_CONSTANT)) { 147 | s->envelope.flags |= ENV_FLAG_START; 148 | s->envelope.divider = s->envelope.volume; 149 | } 150 | } 151 | 152 | static void pulse_write_reg1(PulseState *s, uint8_t reg1) { 153 | s->sweep.period = (reg1 >> 4) & 0x7; 154 | s->sweep.shift = reg1 & 0x7; 155 | s->sweep.flags = 0; 156 | if (reg1 & 0x80) s->sweep.flags |= SWP_FLAG_ENABLED; 157 | if (reg1 & 0x08) s->sweep.flags |= SWP_FLAG_NEGATE; 158 | s->sweep.divider = 0; 159 | } 160 | 161 | static void pulse_write_reg2(PulseState *s, uint8_t reg2) { 162 | s->timer_period &= ~(0xff << TIMER_PREC); 163 | s->timer_period |= reg2 << TIMER_PREC; 164 | } 165 | 166 | static void pulse_write_reg3(PulseState *s, uint8_t reg3) { 167 | s->timer_period &= ~(0x700 << TIMER_PREC); 168 | s->timer_period |= (reg3 & 0x7) << (TIMER_PREC + 8); 169 | s->timer = s->timer_period; 170 | s->length = LENGTH_TABLE[(reg3 >> 3) & 0x1f]; 171 | s->phase = 0; 172 | } 173 | 174 | static void triangle_write_reg0(TriangleState *s, uint8_t reg0) { 175 | s->linear.reload_value = reg0 & 0x7f; 176 | s->linear.flags = 0; 177 | if (reg0 & 0x80) s->linear.flags |= LIN_FLAG_CONTROL; 178 | } 179 | 180 | static void triangle_write_reg2(TriangleState *s, uint8_t reg2) { 181 | s->timer_period &= ~(0xff << TIMER_PREC); 182 | s->timer_period |= reg2 << TIMER_PREC; 183 | } 184 | 185 | static void triangle_write_reg3(TriangleState *s, uint8_t reg3) { 186 | s->timer_period &= ~(0x700 << TIMER_PREC); 187 | s->timer_period |= (reg3 & 0x7) << (TIMER_PREC + 8); 188 | s->timer = s->timer_period; 189 | s->length = LENGTH_TABLE[(reg3 >> 3) & 0x1f]; 190 | s->phase = 0; 191 | s->linear.flags |= LIN_FLAG_RELOAD; 192 | } 193 | 194 | static void noise_write_reg0(NoiseState *s, uint8_t reg0) { 195 | s->envelope.flags = 0; 196 | if (reg0 & 0x10) s->envelope.flags |= ENV_FLAG_CONSTANT; 197 | if (reg0 & 0x20) s->envelope.flags |= ENV_FLAG_HALT_LOOP; 198 | s->envelope.volume = reg0 & 0xf; 199 | 200 | if (!(s->envelope.flags & ENV_FLAG_CONSTANT)) { 201 | s->envelope.flags |= ENV_FLAG_START; 202 | s->envelope.divider = s->envelope.volume; 203 | } 204 | } 205 | 206 | static void noise_write_reg3(NoiseState *s, uint8_t reg3) { 207 | s->length = LENGTH_TABLE[(reg3 >> 3) & 0x1f]; 208 | } 209 | 210 | // see: https://www.nesdev.org/wiki/APU_Envelope 211 | static int step_envelope(Envelope *e) { 212 | if (quarter_frame_step) { 213 | if (e->flags & ENV_FLAG_START) { 214 | e->flags &= ~ENV_FLAG_START; // clear start flag 215 | e->decay = 15; 216 | e->divider = e->volume; 217 | } 218 | else if (e->divider > 0) { 219 | e->divider--; 220 | } 221 | else { 222 | e->divider = e->volume; 223 | if (e->decay > 0) { 224 | e->decay--; 225 | } 226 | else if (e->flags & ENV_FLAG_HALT_LOOP) { 227 | e->decay = 15; 228 | } 229 | } 230 | } 231 | 232 | if (e->flags & ENV_FLAG_CONSTANT) { 233 | return e->volume; 234 | } 235 | else { 236 | return e->decay; 237 | } 238 | } 239 | 240 | // see: https://www.nesdev.org/wiki/APU_Sweep 241 | static int step_sweep(PulseState *s) { 242 | int gate = 1; 243 | if (s->sweep.flags & SWP_FLAG_ENABLED) { 244 | if (half_frame_step) { 245 | if (s->sweep.divider < s->sweep.period) { 246 | s->sweep.divider++; 247 | } 248 | else { 249 | s->sweep.divider = 0; 250 | int change_amount = s->timer_period; 251 | change_amount >>= s->sweep.shift; 252 | if (s->sweep.flags & SWP_FLAG_NEGATE) { 253 | change_amount = -change_amount; 254 | } 255 | int target = s->timer_period + change_amount; 256 | if (target < 0) { 257 | target = 0; 258 | } 259 | else if (target >= (0x7ff << TIMER_PREC)) { 260 | target = (0x7ff << TIMER_PREC); 261 | gate = 0; 262 | } 263 | s->timer_period = target; 264 | } 265 | } 266 | } 267 | return gate; 268 | } 269 | 270 | // see: https://www.nesdev.org/wiki/APU_Triangle 271 | static int step_linear_counter(LinearCounter *c) { 272 | // linear counter 273 | if (quarter_frame_step) { 274 | if (c->flags & LIN_FLAG_RELOAD) { 275 | c->counter = c->reload_value; 276 | } 277 | else if (c->counter > 0) { 278 | c->counter--; 279 | } 280 | 281 | if (!(c->flags & LIN_FLAG_CONTROL)) { 282 | c->flags &= ~LIN_FLAG_RELOAD; // clear reload flag 283 | } 284 | } 285 | return c->counter > 0 ? 1 : 0; 286 | } 287 | 288 | // see: https://www.nesdev.org/wiki/APU_Pulse 289 | static int sample_pulse(PulseState *s) { 290 | // envelope unit 291 | int vol = step_envelope(&(s->envelope)); 292 | 293 | // sweep unit 294 | vol *= step_sweep(s); 295 | 296 | // length counter 297 | if (half_frame_step && s->length > 0 && !(s->envelope.flags & ENV_FLAG_HALT_LOOP)) { 298 | s->length--; 299 | } 300 | 301 | // mute 302 | if (s->timer_period < 8) vol = 0; 303 | if (s->length <= 0) vol = 0; 304 | 305 | // sequencer 306 | if (s->timer >= pulse_timer_step) { 307 | s->timer -= pulse_timer_step; 308 | } 309 | else { 310 | s->timer = s->timer + s->timer_period - pulse_timer_step; 311 | s->phase = (s->phase + 1) & 0x7; 312 | } 313 | int amp = (s->waveform >> s->phase) & 1; 314 | 315 | return amp * vol; 316 | } 317 | 318 | static int sample_triangle(TriangleState *s) { 319 | int vol = 1; 320 | 321 | // length counter 322 | if (half_frame_step && s->length > 0) { 323 | s->length--; 324 | } 325 | 326 | // linear counter 327 | vol *= step_linear_counter(&(s->linear)); 328 | 329 | // phase counter 330 | if (s->timer >= triangle_timer_step) { 331 | s->timer -= triangle_timer_step; 332 | } 333 | else { 334 | s->timer = s->timer + s->timer_period - triangle_timer_step; 335 | s->phase = (s->phase + 1) & 0x1f; 336 | } 337 | 338 | // mute 339 | if (s->timer_period < 8) vol = 0; 340 | if (s->length <= 0) vol = 0; 341 | 342 | // sequencer 343 | if (s->phase <= 15) { 344 | return (15 - s->phase) * vol; 345 | } 346 | else { 347 | return (s->phase - 16) * vol; 348 | } 349 | } 350 | 351 | // see: https://www.nesdev.org/wiki/APU_Pulse 352 | static int sample_noise(NoiseState *s) { 353 | // envelope unit 354 | int vol = step_envelope(&(s->envelope)); 355 | 356 | // length counter 357 | if (half_frame_step && s->length > 0 && !(s->envelope.flags & ENV_FLAG_HALT_LOOP)) { 358 | s->length--; 359 | } 360 | 361 | // mute 362 | if (s->length <= 0) vol = 0; 363 | 364 | // LFSR 365 | int feedback = ((s->lfsr >> 1) ^ s->lfsr) & 1; 366 | s->lfsr = ((s->lfsr >> 1) & 0x3fff) | (feedback << 14); 367 | int amp = s->lfsr & 0xf; 368 | 369 | return (amp * vol) >> 4; 370 | } 371 | 372 | } 373 | -------------------------------------------------------------------------------- /samples/fatfs/source/ffconf.h: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------------/ 2 | / Configurations of FatFs Module 3 | /---------------------------------------------------------------------------*/ 4 | 5 | #define FFCONF_DEF 80286 /* Revision ID */ 6 | 7 | /*---------------------------------------------------------------------------/ 8 | / Function Configurations 9 | /---------------------------------------------------------------------------*/ 10 | 11 | #define FF_FS_READONLY 0 12 | /* This option switches read-only configuration. (0:Read/Write or 1:Read-only) 13 | / Read-only configuration removes writing API functions, f_write(), f_sync(), 14 | / f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree() 15 | / and optional writing functions as well. */ 16 | 17 | 18 | #define FF_FS_MINIMIZE 0 19 | /* This option defines minimization level to remove some basic API functions. 20 | / 21 | / 0: Basic functions are fully enabled. 22 | / 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename() 23 | / are removed. 24 | / 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1. 25 | / 3: f_lseek() function is removed in addition to 2. */ 26 | 27 | 28 | #define FF_USE_FIND 1 29 | /* This option switches filtered directory read functions, f_findfirst() and 30 | / f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */ 31 | 32 | 33 | #define FF_USE_MKFS 0 34 | /* This option switches f_mkfs() function. (0:Disable or 1:Enable) */ 35 | 36 | 37 | #define FF_USE_FASTSEEK 0 38 | /* This option switches fast seek function. (0:Disable or 1:Enable) */ 39 | 40 | 41 | #define FF_USE_EXPAND 0 42 | /* This option switches f_expand function. (0:Disable or 1:Enable) */ 43 | 44 | 45 | #define FF_USE_CHMOD 0 46 | /* This option switches attribute manipulation functions, f_chmod() and f_utime(). 47 | / (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */ 48 | 49 | 50 | #define FF_USE_LABEL 0 51 | /* This option switches volume label functions, f_getlabel() and f_setlabel(). 52 | / (0:Disable or 1:Enable) */ 53 | 54 | 55 | #define FF_USE_FORWARD 0 56 | /* This option switches f_forward() function. (0:Disable or 1:Enable) */ 57 | 58 | 59 | #define FF_USE_STRFUNC 0 60 | #define FF_PRINT_LLI 1 61 | #define FF_PRINT_FLOAT 1 62 | #define FF_STRF_ENCODE 3 63 | /* FF_USE_STRFUNC switches string functions, f_gets(), f_putc(), f_puts() and 64 | / f_printf(). 65 | / 66 | / 0: Disable. FF_PRINT_LLI, FF_PRINT_FLOAT and FF_STRF_ENCODE have no effect. 67 | / 1: Enable without LF-CRLF conversion. 68 | / 2: Enable with LF-CRLF conversion. 69 | / 70 | / FF_PRINT_LLI = 1 makes f_printf() support long long argument and FF_PRINT_FLOAT = 1/2 71 | / makes f_printf() support floating point argument. These features want C99 or later. 72 | / When FF_LFN_UNICODE >= 1 with LFN enabled, string functions convert the character 73 | / encoding in it. FF_STRF_ENCODE selects assumption of character encoding ON THE FILE 74 | / to be read/written via those functions. 75 | / 76 | / 0: ANSI/OEM in current CP 77 | / 1: Unicode in UTF-16LE 78 | / 2: Unicode in UTF-16BE 79 | / 3: Unicode in UTF-8 80 | */ 81 | 82 | 83 | /*---------------------------------------------------------------------------/ 84 | / Locale and Namespace Configurations 85 | /---------------------------------------------------------------------------*/ 86 | 87 | #define FF_CODE_PAGE 932 88 | /* This option specifies the OEM code page to be used on the target system. 89 | / Incorrect code page setting can cause a file open failure. 90 | / 91 | / 437 - U.S. 92 | / 720 - Arabic 93 | / 737 - Greek 94 | / 771 - KBL 95 | / 775 - Baltic 96 | / 850 - Latin 1 97 | / 852 - Latin 2 98 | / 855 - Cyrillic 99 | / 857 - Turkish 100 | / 860 - Portuguese 101 | / 861 - Icelandic 102 | / 862 - Hebrew 103 | / 863 - Canadian French 104 | / 864 - Arabic 105 | / 865 - Nordic 106 | / 866 - Russian 107 | / 869 - Greek 2 108 | / 932 - Japanese (DBCS) 109 | / 936 - Simplified Chinese (DBCS) 110 | / 949 - Korean (DBCS) 111 | / 950 - Traditional Chinese (DBCS) 112 | / 0 - Include all code pages above and configured by f_setcp() 113 | */ 114 | 115 | 116 | #define FF_USE_LFN 1 117 | #define FF_MAX_LFN 255 118 | /* The FF_USE_LFN switches the support for LFN (long file name). 119 | / 120 | / 0: Disable LFN. FF_MAX_LFN has no effect. 121 | / 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe. 122 | / 2: Enable LFN with dynamic working buffer on the STACK. 123 | / 3: Enable LFN with dynamic working buffer on the HEAP. 124 | / 125 | / To enable the LFN, ffunicode.c needs to be added to the project. The LFN function 126 | / requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and 127 | / additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled. 128 | / The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can 129 | / be in range of 12 to 255. It is recommended to be set it 255 to fully support LFN 130 | / specification. 131 | / When use stack for the working buffer, take care on stack overflow. When use heap 132 | / memory for the working buffer, memory management functions, ff_memalloc() and 133 | / ff_memfree() exemplified in ffsystem.c, need to be added to the project. */ 134 | 135 | 136 | #define FF_LFN_UNICODE 0 137 | /* This option switches the character encoding on the API when LFN is enabled. 138 | / 139 | / 0: ANSI/OEM in current CP (TCHAR = char) 140 | / 1: Unicode in UTF-16 (TCHAR = WCHAR) 141 | / 2: Unicode in UTF-8 (TCHAR = char) 142 | / 3: Unicode in UTF-32 (TCHAR = DWORD) 143 | / 144 | / Also behavior of string I/O functions will be affected by this option. 145 | / When LFN is not enabled, this option has no effect. */ 146 | 147 | 148 | #define FF_LFN_BUF 255 149 | #define FF_SFN_BUF 12 150 | /* This set of options defines size of file name members in the FILINFO structure 151 | / which is used to read out directory items. These values should be suffcient for 152 | / the file names to read. The maximum possible length of the read file name depends 153 | / on character encoding. When LFN is not enabled, these options have no effect. */ 154 | 155 | 156 | #define FF_FS_RPATH 0 157 | /* This option configures support for relative path. 158 | / 159 | / 0: Disable relative path and remove related functions. 160 | / 1: Enable relative path. f_chdir() and f_chdrive() are available. 161 | / 2: f_getcwd() function is available in addition to 1. 162 | */ 163 | 164 | 165 | /*---------------------------------------------------------------------------/ 166 | / Drive/Volume Configurations 167 | /---------------------------------------------------------------------------*/ 168 | 169 | #define FF_VOLUMES 1 170 | /* Number of volumes (logical drives) to be used. (1-10) */ 171 | 172 | 173 | #define FF_STR_VOLUME_ID 0 174 | #define FF_VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3" 175 | /* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings. 176 | / When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive 177 | / number in the path name. FF_VOLUME_STRS defines the volume ID strings for each 178 | / logical drives. Number of items must not be less than FF_VOLUMES. Valid 179 | / characters for the volume ID strings are A-Z, a-z and 0-9, however, they are 180 | / compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is 181 | / not defined, a user defined volume string table is needed as: 182 | / 183 | / const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",... 184 | */ 185 | 186 | 187 | #define FF_MULTI_PARTITION 0 188 | /* This option switches support for multiple volumes on the physical drive. 189 | / By default (0), each logical drive number is bound to the same physical drive 190 | / number and only an FAT volume found on the physical drive will be mounted. 191 | / When this function is enabled (1), each logical drive number can be bound to 192 | / arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk() 193 | / function will be available. */ 194 | 195 | 196 | #define FF_MIN_SS 512 197 | #define FF_MAX_SS 512 198 | /* This set of options configures the range of sector size to be supported. (512, 199 | / 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and 200 | / harddisk, but a larger value may be required for on-board flash memory and some 201 | / type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured 202 | / for variable sector size mode and disk_ioctl() function needs to implement 203 | / GET_SECTOR_SIZE command. */ 204 | 205 | 206 | #define FF_LBA64 0 207 | /* This option switches support for 64-bit LBA. (0:Disable or 1:Enable) 208 | / To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */ 209 | 210 | 211 | #define FF_MIN_GPT 0x10000000 212 | /* Minimum number of sectors to switch GPT as partitioning format in f_mkfs and 213 | / f_fdisk function. 0x100000000 max. This option has no effect when FF_LBA64 == 0. */ 214 | 215 | 216 | #define FF_USE_TRIM 0 217 | /* This option switches support for ATA-TRIM. (0:Disable or 1:Enable) 218 | / To enable Trim function, also CTRL_TRIM command should be implemented to the 219 | / disk_ioctl() function. */ 220 | 221 | 222 | 223 | /*---------------------------------------------------------------------------/ 224 | / System Configurations 225 | /---------------------------------------------------------------------------*/ 226 | 227 | #define FF_FS_TINY 0 228 | /* This option switches tiny buffer configuration. (0:Normal or 1:Tiny) 229 | / At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes. 230 | / Instead of private sector buffer eliminated from the file object, common sector 231 | / buffer in the filesystem object (FATFS) is used for the file data transfer. */ 232 | 233 | 234 | #define FF_FS_EXFAT 0 235 | /* This option switches support for exFAT filesystem. (0:Disable or 1:Enable) 236 | / To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1) 237 | / Note that enabling exFAT discards ANSI C (C89) compatibility. */ 238 | 239 | 240 | #define FF_FS_NORTC 1 241 | #define FF_NORTC_MON 1 242 | #define FF_NORTC_MDAY 1 243 | #define FF_NORTC_YEAR 2022 244 | /* The option FF_FS_NORTC switches timestamp feature. If the system does not have 245 | / an RTC or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable the 246 | / timestamp feature. Every object modified by FatFs will have a fixed timestamp 247 | / defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time. 248 | / To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be 249 | / added to the project to read current time form real-time clock. FF_NORTC_MON, 250 | / FF_NORTC_MDAY and FF_NORTC_YEAR have no effect. 251 | / These options have no effect in read-only configuration (FF_FS_READONLY = 1). */ 252 | 253 | 254 | #define FF_FS_NOFSINFO 0 255 | /* If you need to know correct free space on the FAT32 volume, set bit 0 of this 256 | / option, and f_getfree() function at the first time after volume mount will force 257 | / a full FAT scan. Bit 1 controls the use of last allocated cluster number. 258 | / 259 | / bit0=0: Use free cluster count in the FSINFO if available. 260 | / bit0=1: Do not trust free cluster count in the FSINFO. 261 | / bit1=0: Use last allocated cluster number in the FSINFO if available. 262 | / bit1=1: Do not trust last allocated cluster number in the FSINFO. 263 | */ 264 | 265 | 266 | #define FF_FS_LOCK 0 267 | /* The option FF_FS_LOCK switches file lock function to control duplicated file open 268 | / and illegal operation to open objects. This option must be 0 when FF_FS_READONLY 269 | / is 1. 270 | / 271 | / 0: Disable file lock function. To avoid volume corruption, application program 272 | / should avoid illegal open, remove and rename to the open objects. 273 | / >0: Enable file lock function. The value defines how many files/sub-directories 274 | / can be opened simultaneously under file lock control. Note that the file 275 | / lock control is independent of re-entrancy. */ 276 | 277 | 278 | #define FF_FS_REENTRANT 0 279 | #define FF_FS_TIMEOUT 1000 280 | /* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs 281 | / module itself. Note that regardless of this option, file access to different 282 | / volume is always re-entrant and volume control functions, f_mount(), f_mkfs() 283 | / and f_fdisk() function, are always not re-entrant. Only file/directory access 284 | / to the same volume is under control of this featuer. 285 | / 286 | / 0: Disable re-entrancy. FF_FS_TIMEOUT have no effect. 287 | / 1: Enable re-entrancy. Also user provided synchronization handlers, 288 | / ff_mutex_create(), ff_mutex_delete(), ff_mutex_take() and ff_mutex_give() 289 | / function, must be added to the project. Samples are available in ffsystem.c. 290 | / 291 | / The FF_FS_TIMEOUT defines timeout period in unit of O/S time tick. 292 | */ 293 | 294 | 295 | 296 | /*--- End of configuration options ---*/ 297 | -------------------------------------------------------------------------------- /samples/fatfs/source/00history.txt: -------------------------------------------------------------------------------- 1 | ---------------------------------------------------------------------------- 2 | Revision history of FatFs module 3 | ---------------------------------------------------------------------------- 4 | 5 | R0.00 (February 26, 2006) 6 | 7 | Prototype. 8 | 9 | 10 | 11 | R0.01 (April 29, 2006) 12 | 13 | The first release. 14 | 15 | 16 | 17 | R0.02 (June 01, 2006) 18 | 19 | Added FAT12 support. 20 | Removed unbuffered mode. 21 | Fixed a problem on small (<32M) partition. 22 | 23 | 24 | 25 | R0.02a (June 10, 2006) 26 | 27 | Added a configuration option (_FS_MINIMUM). 28 | 29 | 30 | 31 | R0.03 (September 22, 2006) 32 | 33 | Added f_rename(). 34 | Changed option _FS_MINIMUM to _FS_MINIMIZE. 35 | 36 | 37 | 38 | R0.03a (December 11, 2006) 39 | 40 | Improved cluster scan algorithm to write files fast. 41 | Fixed f_mkdir() creates incorrect directory on FAT32. 42 | 43 | 44 | 45 | R0.04 (February 04, 2007) 46 | 47 | Added f_mkfs(). 48 | Supported multiple drive system. 49 | Changed some interfaces for multiple drive system. 50 | Changed f_mountdrv() to f_mount(). 51 | 52 | 53 | 54 | R0.04a (April 01, 2007) 55 | 56 | Supported multiple partitions on a physical drive. 57 | Added a capability of extending file size to f_lseek(). 58 | Added minimization level 3. 59 | Fixed an endian sensitive code in f_mkfs(). 60 | 61 | 62 | 63 | R0.04b (May 05, 2007) 64 | 65 | Added a configuration option _USE_NTFLAG. 66 | Added FSINFO support. 67 | Fixed DBCS name can result FR_INVALID_NAME. 68 | Fixed short seek (<= csize) collapses the file object. 69 | 70 | 71 | 72 | R0.05 (August 25, 2007) 73 | 74 | Changed arguments of f_read(), f_write() and f_mkfs(). 75 | Fixed f_mkfs() on FAT32 creates incorrect FSINFO. 76 | Fixed f_mkdir() on FAT32 creates incorrect directory. 77 | 78 | 79 | 80 | R0.05a (February 03, 2008) 81 | 82 | Added f_truncate() and f_utime(). 83 | Fixed off by one error at FAT sub-type determination. 84 | Fixed btr in f_read() can be mistruncated. 85 | Fixed cached sector is not flushed when create and close without write. 86 | 87 | 88 | 89 | R0.06 (April 01, 2008) 90 | 91 | Added fputc(), fputs(), fprintf() and fgets(). 92 | Improved performance of f_lseek() on moving to the same or following cluster. 93 | 94 | 95 | 96 | R0.07 (April 01, 2009) 97 | 98 | Merged Tiny-FatFs as a configuration option. (_FS_TINY) 99 | Added long file name feature. (_USE_LFN) 100 | Added multiple code page feature. (_CODE_PAGE) 101 | Added re-entrancy for multitask operation. (_FS_REENTRANT) 102 | Added auto cluster size selection to f_mkfs(). 103 | Added rewind option to f_readdir(). 104 | Changed result code of critical errors. 105 | Renamed string functions to avoid name collision. 106 | 107 | 108 | 109 | R0.07a (April 14, 2009) 110 | 111 | Septemberarated out OS dependent code on reentrant cfg. 112 | Added multiple sector size feature. 113 | 114 | 115 | 116 | R0.07c (June 21, 2009) 117 | 118 | Fixed f_unlink() can return FR_OK on error. 119 | Fixed wrong cache control in f_lseek(). 120 | Added relative path feature. 121 | Added f_chdir() and f_chdrive(). 122 | Added proper case conversion to extended character. 123 | 124 | 125 | 126 | R0.07e (November 03, 2009) 127 | 128 | Septemberarated out configuration options from ff.h to ffconf.h. 129 | Fixed f_unlink() fails to remove a sub-directory on _FS_RPATH. 130 | Fixed name matching error on the 13 character boundary. 131 | Added a configuration option, _LFN_UNICODE. 132 | Changed f_readdir() to return the SFN with always upper case on non-LFN cfg. 133 | 134 | 135 | 136 | R0.08 (May 15, 2010) 137 | 138 | Added a memory configuration option. (_USE_LFN = 3) 139 | Added file lock feature. (_FS_SHARE) 140 | Added fast seek feature. (_USE_FASTSEEK) 141 | Changed some types on the API, XCHAR->TCHAR. 142 | Changed .fname in the FILINFO structure on Unicode cfg. 143 | String functions support UTF-8 encoding files on Unicode cfg. 144 | 145 | 146 | 147 | R0.08a (August 16, 2010) 148 | 149 | Added f_getcwd(). (_FS_RPATH = 2) 150 | Added sector erase feature. (_USE_ERASE) 151 | Moved file lock semaphore table from fs object to the bss. 152 | Fixed f_mkfs() creates wrong FAT32 volume. 153 | 154 | 155 | 156 | R0.08b (January 15, 2011) 157 | 158 | Fast seek feature is also applied to f_read() and f_write(). 159 | f_lseek() reports required table size on creating CLMP. 160 | Extended format syntax of f_printf(). 161 | Ignores duplicated directory separators in given path name. 162 | 163 | 164 | 165 | R0.09 (September 06, 2011) 166 | 167 | f_mkfs() supports multiple partition to complete the multiple partition feature. 168 | Added f_fdisk(). 169 | 170 | 171 | 172 | R0.09a (August 27, 2012) 173 | 174 | Changed f_open() and f_opendir() reject null object pointer to avoid crash. 175 | Changed option name _FS_SHARE to _FS_LOCK. 176 | Fixed assertion failure due to OS/2 EA on FAT12/16 volume. 177 | 178 | 179 | 180 | R0.09b (January 24, 2013) 181 | 182 | Added f_setlabel() and f_getlabel(). 183 | 184 | 185 | 186 | R0.10 (October 02, 2013) 187 | 188 | Added selection of character encoding on the file. (_STRF_ENCODE) 189 | Added f_closedir(). 190 | Added forced full FAT scan for f_getfree(). (_FS_NOFSINFO) 191 | Added forced mount feature with changes of f_mount(). 192 | Improved behavior of volume auto detection. 193 | Improved write throughput of f_puts() and f_printf(). 194 | Changed argument of f_chdrive(), f_mkfs(), disk_read() and disk_write(). 195 | Fixed f_write() can be truncated when the file size is close to 4GB. 196 | Fixed f_open(), f_mkdir() and f_setlabel() can return incorrect value on error. 197 | 198 | 199 | 200 | R0.10a (January 15, 2014) 201 | 202 | Added arbitrary strings as drive number in the path name. (_STR_VOLUME_ID) 203 | Added a configuration option of minimum sector size. (_MIN_SS) 204 | 2nd argument of f_rename() can have a drive number and it will be ignored. 205 | Fixed f_mount() with forced mount fails when drive number is >= 1. (appeared at R0.10) 206 | Fixed f_close() invalidates the file object without volume lock. 207 | Fixed f_closedir() returns but the volume lock is left acquired. (appeared at R0.10) 208 | Fixed creation of an entry with LFN fails on too many SFN collisions. (appeared at R0.07) 209 | 210 | 211 | 212 | R0.10b (May 19, 2014) 213 | 214 | Fixed a hard error in the disk I/O layer can collapse the directory entry. 215 | Fixed LFN entry is not deleted when delete/rename an object with lossy converted SFN. (appeared at R0.07) 216 | 217 | 218 | 219 | R0.10c (November 09, 2014) 220 | 221 | Added a configuration option for the platforms without RTC. (_FS_NORTC) 222 | Changed option name _USE_ERASE to _USE_TRIM. 223 | Fixed volume label created by Mac OS X cannot be retrieved with f_getlabel(). (appeared at R0.09b) 224 | Fixed a potential problem of FAT access that can appear on disk error. 225 | Fixed null pointer dereference on attempting to delete the root direcotry. (appeared at R0.08) 226 | 227 | 228 | 229 | R0.11 (February 09, 2015) 230 | 231 | Added f_findfirst(), f_findnext() and f_findclose(). (_USE_FIND) 232 | Fixed f_unlink() does not remove cluster chain of the file. (appeared at R0.10c) 233 | Fixed _FS_NORTC option does not work properly. (appeared at R0.10c) 234 | 235 | 236 | 237 | R0.11a (September 05, 2015) 238 | 239 | Fixed wrong media change can lead a deadlock at thread-safe configuration. 240 | Added code page 771, 860, 861, 863, 864, 865 and 869. (_CODE_PAGE) 241 | Removed some code pages actually not exist on the standard systems. (_CODE_PAGE) 242 | Fixed errors in the case conversion teble of code page 437 and 850 (ff.c). 243 | Fixed errors in the case conversion teble of Unicode (cc*.c). 244 | 245 | 246 | 247 | R0.12 (April 12, 2016) 248 | 249 | Added support for exFAT file system. (_FS_EXFAT) 250 | Added f_expand(). (_USE_EXPAND) 251 | Changed some members in FINFO structure and behavior of f_readdir(). 252 | Added an option _USE_CHMOD. 253 | Removed an option _WORD_ACCESS. 254 | Fixed errors in the case conversion table of Unicode (cc*.c). 255 | 256 | 257 | 258 | R0.12a (July 10, 2016) 259 | 260 | Added support for creating exFAT volume with some changes of f_mkfs(). 261 | Added a file open method FA_OPEN_APPEND. An f_lseek() following f_open() is no longer needed. 262 | f_forward() is available regardless of _FS_TINY. 263 | Fixed f_mkfs() creates wrong volume. (appeared at R0.12) 264 | Fixed wrong memory read in create_name(). (appeared at R0.12) 265 | Fixed compilation fails at some configurations, _USE_FASTSEEK and _USE_FORWARD. 266 | 267 | 268 | 269 | R0.12b (September 04, 2016) 270 | 271 | Made f_rename() be able to rename objects with the same name but case. 272 | Fixed an error in the case conversion teble of code page 866. (ff.c) 273 | Fixed writing data is truncated at the file offset 4GiB on the exFAT volume. (appeared at R0.12) 274 | Fixed creating a file in the root directory of exFAT volume can fail. (appeared at R0.12) 275 | Fixed f_mkfs() creating exFAT volume with too small cluster size can collapse unallocated memory. (appeared at R0.12) 276 | Fixed wrong object name can be returned when read directory at Unicode cfg. (appeared at R0.12) 277 | Fixed large file allocation/removing on the exFAT volume collapses allocation bitmap. (appeared at R0.12) 278 | Fixed some internal errors in f_expand() and f_lseek(). (appeared at R0.12) 279 | 280 | 281 | 282 | R0.12c (March 04, 2017) 283 | 284 | Improved write throughput at the fragmented file on the exFAT volume. 285 | Made memory usage for exFAT be able to be reduced as decreasing _MAX_LFN. 286 | Fixed successive f_getfree() can return wrong count on the FAT12/16 volume. (appeared at R0.12) 287 | Fixed configuration option _VOLUMES cannot be set 10. (appeared at R0.10c) 288 | 289 | 290 | 291 | R0.13 (May 21, 2017) 292 | 293 | Changed heading character of configuration keywords "_" to "FF_". 294 | Removed ASCII-only configuration, FF_CODE_PAGE = 1. Use FF_CODE_PAGE = 437 instead. 295 | Added f_setcp(), run-time code page configuration. (FF_CODE_PAGE = 0) 296 | Improved cluster allocation time on stretch a deep buried cluster chain. 297 | Improved processing time of f_mkdir() with large cluster size by using FF_USE_LFN = 3. 298 | Improved NoFatChain flag of the fragmented file to be set after it is truncated and got contiguous. 299 | Fixed archive attribute is left not set when a file on the exFAT volume is renamed. (appeared at R0.12) 300 | Fixed exFAT FAT entry can be collapsed when write or lseek operation to the existing file is done. (appeared at R0.12c) 301 | Fixed creating a file can fail when a new cluster allocation to the exFAT directory occures. (appeared at R0.12c) 302 | 303 | 304 | 305 | R0.13a (October 14, 2017) 306 | 307 | Added support for UTF-8 encoding on the API. (FF_LFN_UNICODE = 2) 308 | Added options for file name output buffer. (FF_LFN_BUF, FF_SFN_BUF). 309 | Added dynamic memory allocation option for working buffer of f_mkfs() and f_fdisk(). 310 | Fixed f_fdisk() and f_mkfs() create the partition table with wrong CHS parameters. (appeared at R0.09) 311 | Fixed f_unlink() can cause lost clusters at fragmented file on the exFAT volume. (appeared at R0.12c) 312 | Fixed f_setlabel() rejects some valid characters for exFAT volume. (appeared at R0.12) 313 | 314 | 315 | 316 | R0.13b (April 07, 2018) 317 | 318 | Added support for UTF-32 encoding on the API. (FF_LFN_UNICODE = 3) 319 | Added support for Unix style volume ID. (FF_STR_VOLUME_ID = 2) 320 | Fixed accesing any object on the exFAT root directory beyond the cluster boundary can fail. (appeared at R0.12c) 321 | Fixed f_setlabel() does not reject some invalid characters. (appeared at R0.09b) 322 | 323 | 324 | 325 | R0.13c (October 14, 2018) 326 | Supported stdint.h for C99 and later. (integer.h was included in ff.h) 327 | Fixed reading a directory gets infinite loop when the last directory entry is not empty. (appeared at R0.12) 328 | Fixed creating a sub-directory in the fragmented sub-directory on the exFAT volume collapses FAT chain of the parent directory. (appeared at R0.12) 329 | Fixed f_getcwd() cause output buffer overrun when the buffer has a valid drive number. (appeared at R0.13b) 330 | 331 | 332 | 333 | R0.14 (October 14, 2019) 334 | Added support for 64-bit LBA and GUID partition table (FF_LBA64 = 1) 335 | Changed some API functions, f_mkfs() and f_fdisk(). 336 | Fixed f_open() function cannot find the file with file name in length of FF_MAX_LFN characters. 337 | Fixed f_readdir() function cannot retrieve long file names in length of FF_MAX_LFN - 1 characters. 338 | Fixed f_readdir() function returns file names with wrong case conversion. (appeared at R0.12) 339 | Fixed f_mkfs() function can fail to create exFAT volume in the second partition. (appeared at R0.12) 340 | 341 | 342 | R0.14a (December 5, 2020) 343 | Limited number of recursive calls in f_findnext(). 344 | Fixed old floppy disks formatted with MS-DOS 2.x and 3.x cannot be mounted. 345 | Fixed some compiler warnings. 346 | 347 | 348 | 349 | R0.14b (April 17, 2021) 350 | Made FatFs uses standard library for copy, compare and search instead of built-in string functions. 351 | Added support for long long integer and floating point to f_printf(). (FF_STRF_LLI and FF_STRF_FP) 352 | Made path name parser ignore the terminating separator to allow "dir/". 353 | Improved the compatibility in Unix style path name feature. 354 | Fixed the file gets dead-locked when f_open() failed with some conditions. (appeared at R0.12a) 355 | Fixed f_mkfs() can create wrong exFAT volume due to a timing dependent error. (appeared at R0.12) 356 | Fixed code page 855 cannot be set by f_setcp(). 357 | Fixed some compiler warnings. 358 | 359 | 360 | 361 | R0.15 (November 6, 2022) 362 | Changed user provided synchronization functions in order to completely eliminate the platform dependency from FatFs code. 363 | FF_SYNC_t is removed from the configuration options. 364 | Fixed a potential error in f_mount when FF_FS_REENTRANT. 365 | Fixed file lock control FF_FS_LOCK is not mutal excluded when FF_FS_REENTRANT && FF_VOLUMES > 1 is true. 366 | Fixed f_mkfs() creates broken exFAT volume when the size of volume is >= 2^32 sectors. 367 | Fixed string functions cannot write the unicode characters not in BMP when FF_LFN_UNICODE == 2 (UTF-8). 368 | Fixed a compatibility issue in identification of GPT header. 369 | 370 | -------------------------------------------------------------------------------- /samples/fatfs/source/ff.h: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------/ 2 | / FatFs - Generic FAT Filesystem module R0.15 / 3 | /-----------------------------------------------------------------------------/ 4 | / 5 | / Copyright (C) 2022, ChaN, all right reserved. 6 | / 7 | / FatFs module is an open source software. Redistribution and use of FatFs in 8 | / source and binary forms, with or without modification, are permitted provided 9 | / that the following condition is met: 10 | 11 | / 1. Redistributions of source code must retain the above copyright notice, 12 | / this condition and the following disclaimer. 13 | / 14 | / This software is provided by the copyright holder and contributors "AS IS" 15 | / and any warranties related to this software are DISCLAIMED. 16 | / The copyright owner or contributors be NOT LIABLE for any damages caused 17 | / by use of this software. 18 | / 19 | /----------------------------------------------------------------------------*/ 20 | 21 | 22 | #ifndef FF_DEFINED 23 | #define FF_DEFINED 80286 /* Revision ID */ 24 | 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif 28 | 29 | #include "ffconf.h" /* FatFs configuration options */ 30 | 31 | #if FF_DEFINED != FFCONF_DEF 32 | #error Wrong configuration file (ffconf.h). 33 | #endif 34 | 35 | 36 | /* Integer types used for FatFs API */ 37 | 38 | #if defined(_WIN32) /* Windows VC++ (for development only) */ 39 | #define FF_INTDEF 2 40 | #include 41 | typedef unsigned __int64 QWORD; 42 | #include 43 | #define isnan(v) _isnan(v) 44 | #define isinf(v) (!_finite(v)) 45 | 46 | #elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus) /* C99 or later */ 47 | #define FF_INTDEF 2 48 | #include 49 | typedef unsigned int UINT; /* int must be 16-bit or 32-bit */ 50 | typedef unsigned char BYTE; /* char must be 8-bit */ 51 | typedef uint16_t WORD; /* 16-bit unsigned integer */ 52 | typedef uint32_t DWORD; /* 32-bit unsigned integer */ 53 | typedef uint64_t QWORD; /* 64-bit unsigned integer */ 54 | typedef WORD WCHAR; /* UTF-16 character type */ 55 | 56 | #else /* Earlier than C99 */ 57 | #define FF_INTDEF 1 58 | typedef unsigned int UINT; /* int must be 16-bit or 32-bit */ 59 | typedef unsigned char BYTE; /* char must be 8-bit */ 60 | typedef unsigned short WORD; /* 16-bit unsigned integer */ 61 | typedef unsigned long DWORD; /* 32-bit unsigned integer */ 62 | typedef WORD WCHAR; /* UTF-16 character type */ 63 | #endif 64 | 65 | 66 | /* Type of file size and LBA variables */ 67 | 68 | #if FF_FS_EXFAT 69 | #if FF_INTDEF != 2 70 | #error exFAT feature wants C99 or later 71 | #endif 72 | typedef QWORD FSIZE_t; 73 | #if FF_LBA64 74 | typedef QWORD LBA_t; 75 | #else 76 | typedef DWORD LBA_t; 77 | #endif 78 | #else 79 | #if FF_LBA64 80 | #error exFAT needs to be enabled when enable 64-bit LBA 81 | #endif 82 | typedef DWORD FSIZE_t; 83 | typedef DWORD LBA_t; 84 | #endif 85 | 86 | 87 | 88 | /* Type of path name strings on FatFs API (TCHAR) */ 89 | 90 | #if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */ 91 | typedef WCHAR TCHAR; 92 | #define _T(x) L ## x 93 | #define _TEXT(x) L ## x 94 | #elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 encoding */ 95 | typedef char TCHAR; 96 | #define _T(x) u8 ## x 97 | #define _TEXT(x) u8 ## x 98 | #elif FF_USE_LFN && FF_LFN_UNICODE == 3 /* Unicode in UTF-32 encoding */ 99 | typedef DWORD TCHAR; 100 | #define _T(x) U ## x 101 | #define _TEXT(x) U ## x 102 | #elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3) 103 | #error Wrong FF_LFN_UNICODE setting 104 | #else /* ANSI/OEM code in SBCS/DBCS */ 105 | typedef char TCHAR; 106 | #define _T(x) x 107 | #define _TEXT(x) x 108 | #endif 109 | 110 | 111 | 112 | /* Definitions of volume management */ 113 | 114 | #if FF_MULTI_PARTITION /* Multiple partition configuration */ 115 | typedef struct { 116 | BYTE pd; /* Physical drive number */ 117 | BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */ 118 | } PARTITION; 119 | extern PARTITION VolToPart[]; /* Volume - Partition mapping table */ 120 | #endif 121 | 122 | #if FF_STR_VOLUME_ID 123 | #ifndef FF_VOLUME_STRS 124 | extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */ 125 | #endif 126 | #endif 127 | 128 | 129 | 130 | /* Filesystem object structure (FATFS) */ 131 | 132 | typedef struct { 133 | BYTE fs_type; /* Filesystem type (0:not mounted) */ 134 | BYTE pdrv; /* Volume hosting physical drive */ 135 | BYTE ldrv; /* Logical drive number (used only when FF_FS_REENTRANT) */ 136 | BYTE n_fats; /* Number of FATs (1 or 2) */ 137 | BYTE wflag; /* win[] status (b0:dirty) */ 138 | BYTE fsi_flag; /* FSINFO status (b7:disabled, b0:dirty) */ 139 | WORD id; /* Volume mount ID */ 140 | WORD n_rootdir; /* Number of root directory entries (FAT12/16) */ 141 | WORD csize; /* Cluster size [sectors] */ 142 | #if FF_MAX_SS != FF_MIN_SS 143 | WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */ 144 | #endif 145 | #if FF_USE_LFN 146 | WCHAR* lfnbuf; /* LFN working buffer */ 147 | #endif 148 | #if FF_FS_EXFAT 149 | BYTE* dirbuf; /* Directory entry block scratchpad buffer for exFAT */ 150 | #endif 151 | #if !FF_FS_READONLY 152 | DWORD last_clst; /* Last allocated cluster */ 153 | DWORD free_clst; /* Number of free clusters */ 154 | #endif 155 | #if FF_FS_RPATH 156 | DWORD cdir; /* Current directory start cluster (0:root) */ 157 | #if FF_FS_EXFAT 158 | DWORD cdc_scl; /* Containing directory start cluster (invalid when cdir is 0) */ 159 | DWORD cdc_size; /* b31-b8:Size of containing directory, b7-b0: Chain status */ 160 | DWORD cdc_ofs; /* Offset in the containing directory (invalid when cdir is 0) */ 161 | #endif 162 | #endif 163 | DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */ 164 | DWORD fsize; /* Number of sectors per FAT */ 165 | LBA_t volbase; /* Volume base sector */ 166 | LBA_t fatbase; /* FAT base sector */ 167 | LBA_t dirbase; /* Root directory base sector (FAT12/16) or cluster (FAT32/exFAT) */ 168 | LBA_t database; /* Data base sector */ 169 | #if FF_FS_EXFAT 170 | LBA_t bitbase; /* Allocation bitmap base sector */ 171 | #endif 172 | LBA_t winsect; /* Current sector appearing in the win[] */ 173 | BYTE win[FF_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */ 174 | } FATFS; 175 | 176 | 177 | 178 | /* Object ID and allocation information (FFOBJID) */ 179 | 180 | typedef struct { 181 | FATFS* fs; /* Pointer to the hosting volume of this object */ 182 | WORD id; /* Hosting volume's mount ID */ 183 | BYTE attr; /* Object attribute */ 184 | BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous, =3:fragmented in this session, b2:sub-directory stretched) */ 185 | DWORD sclust; /* Object data start cluster (0:no cluster or root directory) */ 186 | FSIZE_t objsize; /* Object size (valid when sclust != 0) */ 187 | #if FF_FS_EXFAT 188 | DWORD n_cont; /* Size of first fragment - 1 (valid when stat == 3) */ 189 | DWORD n_frag; /* Size of last fragment needs to be written to FAT (valid when not zero) */ 190 | DWORD c_scl; /* Containing directory start cluster (valid when sclust != 0) */ 191 | DWORD c_size; /* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */ 192 | DWORD c_ofs; /* Offset in the containing directory (valid when file object and sclust != 0) */ 193 | #endif 194 | #if FF_FS_LOCK 195 | UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */ 196 | #endif 197 | } FFOBJID; 198 | 199 | 200 | 201 | /* File object structure (FIL) */ 202 | 203 | typedef struct { 204 | FFOBJID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */ 205 | BYTE flag; /* File status flags */ 206 | BYTE err; /* Abort flag (error code) */ 207 | FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */ 208 | DWORD clust; /* Current cluster of fpter (invalid when fptr is 0) */ 209 | LBA_t sect; /* Sector number appearing in buf[] (0:invalid) */ 210 | #if !FF_FS_READONLY 211 | LBA_t dir_sect; /* Sector number containing the directory entry (not used at exFAT) */ 212 | BYTE* dir_ptr; /* Pointer to the directory entry in the win[] (not used at exFAT) */ 213 | #endif 214 | #if FF_USE_FASTSEEK 215 | DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */ 216 | #endif 217 | #if !FF_FS_TINY 218 | BYTE buf[FF_MAX_SS]; /* File private data read/write window */ 219 | #endif 220 | } FIL; 221 | 222 | 223 | 224 | /* Directory object structure (DIR) */ 225 | 226 | typedef struct { 227 | FFOBJID obj; /* Object identifier */ 228 | DWORD dptr; /* Current read/write offset */ 229 | DWORD clust; /* Current cluster */ 230 | LBA_t sect; /* Current sector (0:Read operation has terminated) */ 231 | BYTE* dir; /* Pointer to the directory item in the win[] */ 232 | BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */ 233 | #if FF_USE_LFN 234 | DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */ 235 | #endif 236 | #if FF_USE_FIND 237 | const TCHAR* pat; /* Pointer to the name matching pattern */ 238 | #endif 239 | } DIR; 240 | 241 | 242 | 243 | /* File information structure (FILINFO) */ 244 | 245 | typedef struct { 246 | FSIZE_t fsize; /* File size */ 247 | WORD fdate; /* Modified date */ 248 | WORD ftime; /* Modified time */ 249 | BYTE fattrib; /* File attribute */ 250 | #if FF_USE_LFN 251 | TCHAR altname[FF_SFN_BUF + 1];/* Alternative file name */ 252 | TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */ 253 | #else 254 | TCHAR fname[12 + 1]; /* File name */ 255 | #endif 256 | } FILINFO; 257 | 258 | 259 | 260 | /* Format parameter structure (MKFS_PARM) */ 261 | 262 | typedef struct { 263 | BYTE fmt; /* Format option (FM_FAT, FM_FAT32, FM_EXFAT and FM_SFD) */ 264 | BYTE n_fat; /* Number of FATs */ 265 | UINT align; /* Data area alignment (sector) */ 266 | UINT n_root; /* Number of root directory entries */ 267 | DWORD au_size; /* Cluster size (byte) */ 268 | } MKFS_PARM; 269 | 270 | 271 | 272 | /* File function return code (FRESULT) */ 273 | 274 | typedef enum { 275 | FR_OK = 0, /* (0) Succeeded */ 276 | FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */ 277 | FR_INT_ERR, /* (2) Assertion failed */ 278 | FR_NOT_READY, /* (3) The physical drive cannot work */ 279 | FR_NO_FILE, /* (4) Could not find the file */ 280 | FR_NO_PATH, /* (5) Could not find the path */ 281 | FR_INVALID_NAME, /* (6) The path name format is invalid */ 282 | FR_DENIED, /* (7) Access denied due to prohibited access or directory full */ 283 | FR_EXIST, /* (8) Access denied due to prohibited access */ 284 | FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */ 285 | FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */ 286 | FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */ 287 | FR_NOT_ENABLED, /* (12) The volume has no work area */ 288 | FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */ 289 | FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any problem */ 290 | FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */ 291 | FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */ 292 | FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */ 293 | FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > FF_FS_LOCK */ 294 | FR_INVALID_PARAMETER /* (19) Given parameter is invalid */ 295 | } FRESULT; 296 | 297 | 298 | 299 | 300 | /*--------------------------------------------------------------*/ 301 | /* FatFs Module Application Interface */ 302 | /*--------------------------------------------------------------*/ 303 | 304 | FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */ 305 | FRESULT f_close (FIL* fp); /* Close an open file object */ 306 | FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */ 307 | FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */ 308 | FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */ 309 | FRESULT f_truncate (FIL* fp); /* Truncate the file */ 310 | FRESULT f_sync (FIL* fp); /* Flush cached data of the writing file */ 311 | FRESULT f_opendir (DIR* dp, const TCHAR* path); /* Open a directory */ 312 | FRESULT f_closedir (DIR* dp); /* Close an open directory */ 313 | FRESULT f_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */ 314 | FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */ 315 | FRESULT f_findnext (DIR* dp, FILINFO* fno); /* Find next file */ 316 | FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */ 317 | FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */ 318 | FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */ 319 | FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */ 320 | FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */ 321 | FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change timestamp of a file/dir */ 322 | FRESULT f_chdir (const TCHAR* path); /* Change current directory */ 323 | FRESULT f_chdrive (const TCHAR* path); /* Change current drive */ 324 | FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */ 325 | FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */ 326 | FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */ 327 | FRESULT f_setlabel (const TCHAR* label); /* Set volume label */ 328 | FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */ 329 | FRESULT f_expand (FIL* fp, FSIZE_t fsz, BYTE opt); /* Allocate a contiguous block to the file */ 330 | FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */ 331 | FRESULT f_mkfs (const TCHAR* path, const MKFS_PARM* opt, void* work, UINT len); /* Create a FAT volume */ 332 | FRESULT f_fdisk (BYTE pdrv, const LBA_t ptbl[], void* work); /* Divide a physical drive into some partitions */ 333 | FRESULT f_setcp (WORD cp); /* Set current code page */ 334 | int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */ 335 | int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */ 336 | int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */ 337 | TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */ 338 | 339 | /* Some API fucntions are implemented as macro */ 340 | 341 | #define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize)) 342 | #define f_error(fp) ((fp)->err) 343 | #define f_tell(fp) ((fp)->fptr) 344 | #define f_size(fp) ((fp)->obj.objsize) 345 | #define f_rewind(fp) f_lseek((fp), 0) 346 | #define f_rewinddir(dp) f_readdir((dp), 0) 347 | #define f_rmdir(path) f_unlink(path) 348 | #define f_unmount(path) f_mount(0, path, 0) 349 | 350 | 351 | 352 | 353 | /*--------------------------------------------------------------*/ 354 | /* Additional Functions */ 355 | /*--------------------------------------------------------------*/ 356 | 357 | /* RTC function (provided by user) */ 358 | #if !FF_FS_READONLY && !FF_FS_NORTC 359 | DWORD get_fattime (void); /* Get current time */ 360 | #endif 361 | 362 | 363 | /* LFN support functions (defined in ffunicode.c) */ 364 | 365 | #if FF_USE_LFN >= 1 366 | WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode conversion */ 367 | WCHAR ff_uni2oem (DWORD uni, WORD cp); /* Unicode to OEM code conversion */ 368 | DWORD ff_wtoupper (DWORD uni); /* Unicode upper-case conversion */ 369 | #endif 370 | 371 | 372 | /* O/S dependent functions (samples available in ffsystem.c) */ 373 | 374 | #if FF_USE_LFN == 3 /* Dynamic memory allocation */ 375 | void* ff_memalloc (UINT msize); /* Allocate memory block */ 376 | void ff_memfree (void* mblock); /* Free memory block */ 377 | #endif 378 | #if FF_FS_REENTRANT /* Sync functions */ 379 | int ff_mutex_create (int vol); /* Create a sync object */ 380 | void ff_mutex_delete (int vol); /* Delete a sync object */ 381 | int ff_mutex_take (int vol); /* Lock sync object */ 382 | void ff_mutex_give (int vol); /* Unlock sync object */ 383 | #endif 384 | 385 | 386 | 387 | 388 | /*--------------------------------------------------------------*/ 389 | /* Flags and Offset Address */ 390 | /*--------------------------------------------------------------*/ 391 | 392 | /* File access mode and open method flags (3rd argument of f_open) */ 393 | #define FA_READ 0x01 394 | #define FA_WRITE 0x02 395 | #define FA_OPEN_EXISTING 0x00 396 | #define FA_CREATE_NEW 0x04 397 | #define FA_CREATE_ALWAYS 0x08 398 | #define FA_OPEN_ALWAYS 0x10 399 | #define FA_OPEN_APPEND 0x30 400 | 401 | /* Fast seek controls (2nd argument of f_lseek) */ 402 | #define CREATE_LINKMAP ((FSIZE_t)0 - 1) 403 | 404 | /* Format options (2nd argument of f_mkfs) */ 405 | #define FM_FAT 0x01 406 | #define FM_FAT32 0x02 407 | #define FM_EXFAT 0x04 408 | #define FM_ANY 0x07 409 | #define FM_SFD 0x08 410 | 411 | /* Filesystem type (FATFS.fs_type) */ 412 | #define FS_FAT12 1 413 | #define FS_FAT16 2 414 | #define FS_FAT32 3 415 | #define FS_EXFAT 4 416 | 417 | /* File attribute bits for directory entry (FILINFO.fattrib) */ 418 | #define AM_RDO 0x01 /* Read only */ 419 | #define AM_HID 0x02 /* Hidden */ 420 | #define AM_SYS 0x04 /* System */ 421 | #define AM_DIR 0x10 /* Directory */ 422 | #define AM_ARC 0x20 /* Archive */ 423 | 424 | 425 | #ifdef __cplusplus 426 | } 427 | #endif 428 | 429 | #endif /* FF_DEFINED */ 430 | -------------------------------------------------------------------------------- /core/src/ppu.cpp: -------------------------------------------------------------------------------- 1 | #include "shapones/shapones.hpp" 2 | 3 | namespace nes::ppu { 4 | 5 | static Registers reg; 6 | //static RenderContext ctx; 7 | 8 | static volatile cycle_t cycle_count; 9 | 10 | static int focus_x; 11 | static int focus_y; 12 | 13 | //static ScrollRegister scroll; 14 | static uint16_t scroll; 15 | static int fine_x; 16 | 17 | static uint8_t bus_read_data_latest = 0; 18 | static uint8_t bus_read_data_delayed = 0; 19 | 20 | static bool scroll_ppuaddr_high_stored = false; 21 | 22 | //static uint8_t line_buff[SCREEN_WIDTH]; 23 | 24 | static Palette palette_file[PALETTE_NUM_BANK]; 25 | 26 | static OamEntry oam[MAX_SPRITE_COUNT]; 27 | static SpriteLine sprite_lines[MAX_VISIBLE_SPRITES]; 28 | static int num_visible_sprites; 29 | 30 | static uint8_t bus_read(addr_t addr); 31 | static void bus_write(addr_t addr, uint8_t data); 32 | static uint8_t oam_read(addr_t addr); 33 | static void oam_write(addr_t addr, uint8_t data); 34 | static uint8_t palette_read(addr_t addr); 35 | static void palette_write(addr_t addr, uint8_t data); 36 | 37 | static void render_bg(uint8_t *line_buff, int x0, int x1); 38 | static void enum_visible_sprites(); 39 | static void render_sprite(uint8_t *line_buff, int x0, int x1); 40 | 41 | void reset() { 42 | // https://www.nesdev.org/wiki/PPU_power_up_state 43 | reg.control.raw = 0; 44 | reg.mask.raw = 0; 45 | reg.oam_addr = 0; 46 | reg.scroll = 0; 47 | reg.status.raw = 0; 48 | reg.fine_x = 0; 49 | 50 | bus_read_data_latest = 0; 51 | bus_read_data_delayed = 0; 52 | 53 | focus_x = 0; 54 | focus_y = 0; 55 | 56 | num_visible_sprites = 0; 57 | 58 | scroll_ppuaddr_high_stored = false; 59 | } 60 | 61 | bool is_in_hblank() { 62 | return focus_x >= SCREEN_WIDTH; 63 | } 64 | 65 | int current_focus_y() { 66 | return focus_y; 67 | } 68 | 69 | // todo: exclusive control 70 | uint8_t reg_read(addr_t addr) { 71 | uint8_t retval; 72 | switch(addr) { 73 | case REG_PPUSTATUS: 74 | { 75 | retval = reg.status.raw; 76 | reg.status.vblank_flag = 0; 77 | scroll_ppuaddr_high_stored = false; 78 | } 79 | break; 80 | 81 | case REG_OAMDATA: 82 | retval = oam_read(reg.oam_addr); 83 | break; 84 | 85 | case REG_PPUDATA: 86 | { 87 | addr_t addr = scroll & SCROLL_MASK_PPU_ADDR; 88 | bus_read(addr); 89 | addr += reg.control.incr_stride ? 32 : 1; 90 | addr &= SCROLL_MASK_PPU_ADDR; 91 | scroll &= ~SCROLL_MASK_PPU_ADDR; 92 | scroll |= addr; 93 | retval = bus_read_data_delayed; 94 | } 95 | break; 96 | 97 | default: 98 | NES_PRINTF("*Warning: Invalid PPU register read addr: 0x%x\n", addr); 99 | retval = 0; 100 | break; 101 | } 102 | return retval; 103 | } 104 | 105 | // todo: exclusive control 106 | void reg_write(addr_t addr, uint8_t data) { 107 | switch(addr) { 108 | case REG_PPUCTRL: 109 | // name sel bits 110 | reg.scroll &= 0xf3ff; 111 | reg.scroll |= (uint16_t)(data & 0x3) << 10; 112 | // other bits 113 | reg.control.raw = data; 114 | break; 115 | 116 | case REG_PPUMASK: 117 | reg.mask.raw = data; 118 | break; 119 | 120 | case REG_OAMADDR: 121 | reg.oam_addr = data; 122 | break; 123 | 124 | case REG_OAMDATA: 125 | oam_write(reg.oam_addr, data); 126 | break; 127 | 128 | case REG_PPUSCROLL: 129 | if ( ! scroll_ppuaddr_high_stored) { 130 | reg.scroll &= ~SCROLL_MASK_COARSE_X; 131 | reg.scroll |= (data >> 3) & SCROLL_MASK_COARSE_X; 132 | reg.fine_x = data & 0x7; 133 | scroll_ppuaddr_high_stored = true; 134 | } 135 | else { 136 | reg.scroll &= ~(SCROLL_MASK_COARSE_Y | SCROLL_MASK_FINE_Y); 137 | reg.scroll |= ((uint16_t)data << 2) & SCROLL_MASK_COARSE_Y; 138 | reg.scroll |= ((uint16_t)data << 12) & SCROLL_MASK_FINE_Y; 139 | scroll_ppuaddr_high_stored = false; 140 | } 141 | break; 142 | 143 | case REG_PPUADDR: 144 | if ( ! scroll_ppuaddr_high_stored) { 145 | reg.scroll &= 0x00ffu; 146 | reg.scroll |= ((uint16_t)data << 8) & 0x3f00u; 147 | scroll_ppuaddr_high_stored = true; 148 | } 149 | else { 150 | reg.scroll &= 0xff00u; 151 | reg.scroll |= (uint16_t)data; 152 | scroll = reg.scroll; 153 | scroll_ppuaddr_high_stored = false; 154 | } 155 | break; 156 | 157 | case REG_PPUDATA: 158 | { 159 | addr_t addr = scroll & SCROLL_MASK_PPU_ADDR; 160 | bus_write(addr, data); 161 | addr += reg.control.incr_stride ? 32 : 1; 162 | addr &= SCROLL_MASK_PPU_ADDR; 163 | scroll &= ~SCROLL_MASK_PPU_ADDR; 164 | scroll |= addr; 165 | } 166 | break; 167 | 168 | default: 169 | NES_PRINTF("*Warning: Invalid PPU register write addr: 0x%x\n", addr); 170 | } 171 | } 172 | 173 | void oam_dma_write(addr_t offset, uint8_t data) { 174 | oam_write((reg.oam_addr + offset) % OAM_SIZE, data); 175 | } 176 | 177 | static uint8_t bus_read(addr_t addr) { 178 | if (CHRROM_BASE <= addr && addr < CHRROM_BASE + CHRROM_SIZE) { 179 | bus_read_data_delayed = bus_read_data_latest; 180 | bus_read_data_latest = memory::chrrom_read(addr - CHRROM_BASE); 181 | } 182 | else if (VRAM_BASE <= addr && addr < VRAM_BASE + memory::VRAM_SIZE) { 183 | bus_read_data_delayed = bus_read_data_latest; 184 | bus_read_data_latest = memory::vram_read(addr - VRAM_BASE); 185 | } 186 | else if (PALETTE_FILE_BASE <= addr && addr < PALETTE_FILE_BASE + PALETTE_FILE_SIZE_WITH_MIRROR) { 187 | bus_read_data_delayed = palette_read(addr - PALETTE_FILE_BASE); 188 | bus_read_data_latest = bus_read_data_delayed; 189 | } 190 | else if (VRAM_MIRROR_BASE <= addr && addr < VRAM_MIRROR_BASE + VRAM_MIRROR_SIZE) { 191 | bus_read_data_delayed = bus_read_data_latest; 192 | bus_read_data_latest = memory::vram_read(addr - VRAM_MIRROR_BASE); 193 | } 194 | else { 195 | bus_read_data_delayed = 0; 196 | bus_read_data_latest = 0; 197 | } 198 | return bus_read_data_latest; 199 | } 200 | 201 | static void bus_write(addr_t addr, uint8_t data) { 202 | if (VRAM_BASE <= addr && addr < VRAM_BASE + memory::VRAM_SIZE) { 203 | memory::vram_write(addr - VRAM_BASE, data); 204 | } 205 | else if (PALETTE_FILE_BASE <= addr && addr < PALETTE_FILE_BASE + PALETTE_FILE_SIZE_WITH_MIRROR) { 206 | palette_write(addr - PALETTE_FILE_BASE, data); 207 | } 208 | else if (VRAM_MIRROR_BASE <= addr && addr < VRAM_MIRROR_BASE + VRAM_MIRROR_SIZE) { 209 | memory::vram_write(addr - VRAM_MIRROR_BASE, data); 210 | } 211 | else { 212 | //NES_PRINTF("*Warning: Invalid PPU bus write addr: 0x%x\n", addr); 213 | } 214 | } 215 | 216 | static uint8_t oam_read(addr_t addr) { 217 | switch (addr & 0x3) { 218 | case 0: return oam[addr / 4].y; 219 | case 1: return oam[addr / 4].tile; 220 | case 2: return oam[addr / 4].attr; 221 | default: return oam[addr / 4].x; 222 | } 223 | } 224 | 225 | static void oam_write(addr_t addr, uint8_t data) { 226 | switch (addr & 0x3) { 227 | case 0: oam[addr / 4].y = data; break; 228 | case 1: oam[addr / 4].tile = data; break; 229 | case 2: oam[addr / 4].attr = data; break; 230 | default: oam[addr / 4].x = data; break; 231 | } 232 | } 233 | 234 | static uint8_t palette_read(addr_t addr) { 235 | addr %= PALETTE_FILE_SIZE; 236 | switch (addr) { 237 | case 0x10: return palette_file[0].color[0]; 238 | case 0x14: return palette_file[1].color[0]; 239 | case 0x18: return palette_file[2].color[0]; 240 | case 0x1c: return palette_file[3].color[0]; 241 | default: return palette_file[addr / 4].color[addr & 0x3]; 242 | } 243 | } 244 | 245 | static void palette_write(addr_t addr, uint8_t data) { 246 | addr %= PALETTE_FILE_SIZE; 247 | data &= 0x3f; 248 | 249 | switch (addr) { 250 | case 0x10: palette_file[0].color[0] = data; break; 251 | case 0x14: palette_file[1].color[0] = data; break; 252 | case 0x18: palette_file[2].color[0] = data; break; 253 | case 0x1c: palette_file[3].color[0] = data; break; 254 | default: palette_file[addr / 4].color[addr & 0x3] = data; break; 255 | } 256 | } 257 | 258 | bool service(uint8_t *line_buff) { 259 | bool eol = false; 260 | 261 | while (true) { 262 | cycle_t cycle_diff = cpu::ppu_cycle_leading() - cycle_count; 263 | if (cycle_diff <= 0) { 264 | break; 265 | } 266 | 267 | // events 268 | if (focus_x == 0) { 269 | if (focus_y == SCREEN_HEIGHT + 1) { 270 | // vblank flag/interrupt 271 | reg.status.vblank_flag = 1; 272 | if (reg.control.vblank_irq_enable) { 273 | interrupt::assert_nmi(); 274 | } 275 | } 276 | else if (focus_y == SCAN_LINES - 1) { 277 | // clear flags 278 | reg.status.vblank_flag = 0; 279 | reg.status.sprite0_hit = 0; 280 | } 281 | } 282 | 283 | // determine step count 284 | int step_count; 285 | int dist_to_end; 286 | if (focus_y < SCREEN_HEIGHT && focus_x < SCREEN_WIDTH) { 287 | // visible area 288 | dist_to_end = SCREEN_WIDTH - focus_x; 289 | } 290 | else { 291 | // blank_area 292 | dist_to_end = LINE_CYCLES - focus_x; 293 | } 294 | step_count = dist_to_end < cycle_diff ? dist_to_end : cycle_diff; 295 | step_count = step_count < MAX_DELAY_CYCLES/2 ? step_count : MAX_DELAY_CYCLES/2; 296 | 297 | int next_focus_x = focus_x + step_count; 298 | 299 | // render background 300 | render_bg(line_buff, focus_x, next_focus_x); 301 | 302 | if (reg.mask.sprite_enable && focus_x < SCREEN_WIDTH && focus_y < SCREEN_HEIGHT) { 303 | if (focus_x == 0) { 304 | // enumerate visible sprites in current line 305 | enum_visible_sprites(); 306 | } 307 | // render sprite 308 | render_sprite(line_buff, focus_x, next_focus_x); 309 | } 310 | 311 | // step focus 312 | focus_x = next_focus_x; 313 | if (focus_x >= LINE_CYCLES) { 314 | focus_x -= LINE_CYCLES; 315 | focus_y++; 316 | if (focus_y >= SCAN_LINES) { 317 | focus_y = 0; 318 | } 319 | } 320 | 321 | // step cycle counter 322 | cycle_count += step_count; 323 | 324 | eol = 325 | (focus_x == SCREEN_WIDTH && focus_y < SCREEN_HEIGHT) || 326 | (focus_x == 0 && focus_y >= SCREEN_HEIGHT); 327 | if (eol) { 328 | //NES_PRINTF("EOL y=%d\n", focus_y); 329 | break; 330 | } 331 | } 332 | 333 | return eol; 334 | } 335 | 336 | static void render_bg(uint8_t *line_buff, int x0_block, int x1_block) { 337 | bool visible_area = (x0_block < SCREEN_WIDTH && focus_y < SCREEN_HEIGHT); 338 | 339 | while (x0_block < x1_block) { 340 | // determine step count 341 | int x0, x1; 342 | 343 | x0 = x0_block; 344 | if (visible_area && reg.mask.bg_enable) { 345 | x1 = x0 + (BLOCK_SIZE - ((scroll & 0x1) * TILE_SIZE + fine_x)); 346 | } 347 | else { 348 | x1 = x0 + 64; 349 | } 350 | x1 = x1 < x1_block ? x1 : x1_block; 351 | 352 | if (visible_area) { 353 | if (reg.mask.bg_enable) { 354 | // read name table for two tiles 355 | addr_t name_addr0 = scroll & 0xffeu; 356 | addr_t name_addr1 = name_addr0 + 1; 357 | uint8_t name0 = memory::vram_read(name_addr0); 358 | uint8_t name1 = memory::vram_read(name_addr1); 359 | 360 | int fine_y = (scroll & SCROLL_MASK_FINE_Y) >> 12; 361 | 362 | // read CHRROM 363 | int chrrom_index0 = name0 * 8 + fine_y; 364 | int chrrom_index1 = name1 * 8 + fine_y; 365 | if (reg.control.bg_name_sel) { 366 | chrrom_index1 += 0x800; 367 | chrrom_index0 += 0x800; 368 | } 369 | uint16_t chr0 = memory::chrrom_read_w(chrrom_index0, false); 370 | uint16_t chr1 = memory::chrrom_read_w(chrrom_index1, false); 371 | 372 | uint32_t chr = (uint32_t)chr0 | ((uint32_t)chr1 << 16); 373 | 374 | // calc attr index 375 | int attr_index = 376 | (scroll & SCROLL_MASK_NAME_SEL) | 377 | 0x3c0 | 378 | ((scroll >> 2) & 0x7) | 379 | ((scroll >> 4) & 0x38); 380 | int attr_shift_size = 381 | ((scroll >> 4) & 0x4) | 382 | (scroll & 0x2); 383 | 384 | // read attr table 385 | uint8_t attr = memory::vram_read(attr_index); 386 | attr = (attr >> attr_shift_size) & 0x3; 387 | Palette palette = palette_file[attr]; 388 | 389 | // adjust CHR bit pos 390 | int chr_shift_size = 391 | ((scroll << 4) & 0x10) | 392 | (fine_x << 1); 393 | chr >>= chr_shift_size; 394 | 395 | // render BG block 396 | uint8_t bg_color = palette_file[0].color[0]; 397 | for (int x = x0; x < x1; x++) { 398 | uint32_t palette_index = chr & 0x3; 399 | chr >>= 2; 400 | if (palette_index == 0) { 401 | line_buff[x] = bg_color; 402 | } 403 | else { 404 | line_buff[x] = palette.color[palette_index] | OPAQUE_FLAG; 405 | } 406 | } 407 | } 408 | else { 409 | // blank background 410 | uint8_t bg_color = palette_file[0].color[0]; 411 | for (int x = x0; x < x1; x++) { 412 | line_buff[x] = bg_color; 413 | } 414 | } 415 | } 416 | 417 | // update scroll counter 418 | // see: https://www.nesdev.org/wiki/PPU_scrolling 419 | if (reg.mask.bg_enable || reg.mask.sprite_enable) { 420 | if (focus_y < SCREEN_HEIGHT) { 421 | if (x0 < SCREEN_WIDTH) { 422 | // step scroll counter for x-axis 423 | fine_x += (x1 - x0); 424 | while (fine_x >= TILE_SIZE) { 425 | fine_x -= TILE_SIZE; 426 | // if coarse_x < 31 427 | if ((scroll & SCROLL_MASK_COARSE_X) < SCROLL_MASK_COARSE_X) { 428 | scroll++; // coarse_x++ 429 | } 430 | else { 431 | // right edge of name table 432 | scroll &= ~SCROLL_MASK_COARSE_X; // coarse_x = 0 433 | scroll ^= 0x0400u; // switch name table horizontally 434 | } 435 | } 436 | } 437 | else if (SCREEN_WIDTH < x1 && x0 <= SCREEN_WIDTH) { 438 | // step scroll counter for y-axis 439 | // if fine_y < 7 440 | if ((scroll & SCROLL_MASK_FINE_Y) < SCROLL_MASK_FINE_Y) { 441 | scroll += 0x1000u; // fine_y++ 442 | } 443 | else { 444 | // bottom edge of tile 445 | scroll &= ~SCROLL_MASK_FINE_Y; // fine_y = 0 446 | // if coarse_y == 29 447 | if ((scroll & SCROLL_MASK_COARSE_Y) == ((NUM_TILE_Y - 1) << 5)) { 448 | // bottom edge of name table 449 | scroll &= ~SCROLL_MASK_COARSE_Y; // coarse_y = 0 450 | scroll ^= 0x0800u; // switch name table vertically 451 | } 452 | // else if coarse_y == 31 453 | else if ((scroll & SCROLL_MASK_COARSE_Y) == SCROLL_MASK_COARSE_Y) { 454 | scroll &= ~SCROLL_MASK_COARSE_Y; // coarse_y = 0 455 | } 456 | else { 457 | scroll += NUM_TILE_X; // coarse_y++ 458 | } 459 | } 460 | 461 | // horizontal recovery 462 | constexpr uint16_t copy_mask = 0x041fu; 463 | scroll &= ~copy_mask; 464 | scroll |= reg.scroll & copy_mask; 465 | fine_x = reg.fine_x; 466 | } 467 | } 468 | else if (focus_y == SCAN_LINES - 1) { 469 | if (280 <= x1 && x0 <= 304) { 470 | // vertical recovery 471 | constexpr uint16_t copy_mask = 0x7be0u; 472 | scroll &= ~copy_mask; 473 | scroll |= reg.scroll & copy_mask; 474 | } 475 | } 476 | } // if 477 | 478 | x0_block = x1; 479 | } // while 480 | } 481 | 482 | static void enum_visible_sprites() { 483 | num_visible_sprites = 0; 484 | uint8_t h = reg.control.sprite_size ? 16 : 8; 485 | for (int i = 0; i < MAX_SPRITE_COUNT; i++) { 486 | auto s = oam[i]; 487 | 488 | // vertical hit test 489 | if (s.y + SPRITE_Y_OFFSET <= focus_y && focus_y < s.y + h + SPRITE_Y_OFFSET) { 490 | int src_y = focus_y - (s.y + SPRITE_Y_OFFSET); 491 | 492 | if (s.attr & OAM_ATTR_INVERT_V) { 493 | // vertical inversion 494 | if (reg.control.sprite_size) { 495 | src_y ^= 0xf; 496 | } 497 | else { 498 | src_y ^= 0x7; 499 | } 500 | } 501 | 502 | // tile index calculation 503 | int tile_index; 504 | if (reg.control.sprite_size) { 505 | // 8x16 sprite 506 | if (src_y < 8) { 507 | tile_index = s.tile & 0xfe; 508 | } 509 | else { 510 | tile_index = s.tile | 0x01; 511 | } 512 | 513 | if (s.tile & 0x1) { 514 | tile_index += 0x1000 / 16; 515 | } 516 | } 517 | else { 518 | // 8x8 sprite 519 | tile_index = s.tile; 520 | if (reg.control.sprite_name_sel) { 521 | tile_index += 0x1000 / 16; 522 | } 523 | } 524 | 525 | // read CHRROM 526 | int chrrom_index = tile_index * 8 + (src_y & 0x7); 527 | uint16_t chr; 528 | chr = memory::chrrom_read_w(chrrom_index, s.attr & OAM_ATTR_INVERT_H); 529 | 530 | // store sprite information 531 | SpriteLine sl; 532 | sl.chr = chr; 533 | sl.x = s.x; 534 | sl.palette = palette_file[4 + (s.attr & OAM_ATTR_PALETTE)]; 535 | sl.attr = 0; 536 | if (s.attr & OAM_ATTR_PRIORITY) sl.attr |= SL_ATTR_BEHIND; 537 | if (i == 0) sl.attr |= SL_ATTR_ZERO; 538 | sprite_lines[num_visible_sprites++] = sl; 539 | 540 | if (num_visible_sprites >= MAX_VISIBLE_SPRITES) { 541 | break; 542 | } 543 | } 544 | } 545 | } 546 | 547 | static void render_sprite(uint8_t *line_buff, int x0_block, int x1_block) { 548 | // sprite height 549 | int h = reg.control.sprite_size ? 16 : 8; 550 | for (int i = 0; i < num_visible_sprites; i++) { 551 | auto sl = sprite_lines[i]; 552 | int x0 = x0_block > sl.x ? x0_block : sl.x; 553 | int x1 = x1_block < sl.x + TILE_SIZE ? x1_block : sl.x + TILE_SIZE; 554 | uint16_t chr = sl.chr >> (2 * (x0 - sl.x)); 555 | for (int x = x0; x < x1; x++) { 556 | uint16_t palette_index = chr & 0x3; 557 | chr >>= 2; 558 | bool sprite_opaque = (palette_index != 0); 559 | bool bg_opaque = (line_buff[x] & OPAQUE_FLAG) != 0; 560 | if (sprite_opaque && (!bg_opaque || !(sl.attr & SL_ATTR_BEHIND))) { 561 | line_buff[x] = sl.palette.color[palette_index]; 562 | } 563 | if ((sl.attr & SL_ATTR_ZERO) && sprite_opaque && bg_opaque) { 564 | reg.status.sprite0_hit = 1; 565 | } 566 | } 567 | } 568 | } 569 | 570 | cycle_t cycle_following() { 571 | return cycle_count; 572 | } 573 | 574 | } 575 | -------------------------------------------------------------------------------- /samples/fatfs/source/mmc_pico_spi.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------*/ 2 | /* MMCv3/SDv1/SDv2 (SPI mode) control module */ 3 | /*------------------------------------------------------------------------*/ 4 | /* 5 | / Copyright (C) 2018, ChaN, all right reserved. 6 | / 7 | / * This software is a free software and there is NO WARRANTY. 8 | / * No restriction on use. You can use, modify and redistribute it for 9 | / personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY. 10 | / * Redistributions of source code must retain the above copyright notice. 11 | / 12 | /-------------------------------------------------------------------------*/ 13 | 14 | // changed for Raspberry Pi pico 15 | // see: https://qiita.com/Yukiya_Ishioka/items/6b5b6cb246f1d1e94461 16 | 17 | #include "pico/stdlib.h" 18 | #include "ws19804_c.h" 19 | 20 | #define DEF_SPI_TX_PIN 11 21 | #define DEF_SPI_RX_PIN 12 22 | #define DEF_SPI_SCK_PIN 10 23 | #define DEF_SPI_CSN_PIN 22 24 | 25 | #define FCLK_FAST() { } 26 | #define FCLK_SLOW() { } 27 | 28 | #define CS_HIGH() { gpio_put(DEF_SPI_CSN_PIN, 1 ); /* HIGH */ } 29 | #define CS_LOW() { gpio_put(DEF_SPI_CSN_PIN, 0 ); /* LOW */ } 30 | 31 | #define MMC_CD 1 /* Card detect (yes:true, no:false, default:true) */ 32 | #define MMC_WP 0 /* Write protected (yes:true, no:false, default:false) */ 33 | 34 | 35 | /*-------------------------------------------------------------------------- 36 | 37 | Module Private Functions 38 | 39 | ---------------------------------------------------------------------------*/ 40 | #include "ff.h" /* Obtains integer types */ 41 | #include "diskio.h" /* Declarations of disk functions */ 42 | 43 | 44 | /* MMC card type flags (MMC_GET_TYPE) */ 45 | #define CT_MMC3 0x01 /* MMC ver 3 */ 46 | #define CT_MMC4 0x02 /* MMC ver 4+ */ 47 | #define CT_MMC 0x03 /* MMC */ 48 | #define CT_SDC1 0x02 /* SDC ver 1 */ 49 | #define CT_SDC2 0x04 /* SDC ver 2+ */ 50 | #define CT_SDC 0x0C /* SDC */ 51 | #define CT_BLOCK 0x10 /* Block addressing */ 52 | 53 | 54 | /* MMC/SD command */ 55 | #define CMD0 (0) /* GO_IDLE_STATE */ 56 | #define CMD1 (1) /* SEND_OP_COND (MMC) */ 57 | #define ACMD41 (0x80+41) /* SEND_OP_COND (SDC) */ 58 | #define CMD8 (8) /* SEND_IF_COND */ 59 | #define CMD9 (9) /* SEND_CSD */ 60 | #define CMD10 (10) /* SEND_CID */ 61 | #define CMD12 (12) /* STOP_TRANSMISSION */ 62 | #define ACMD13 (0x80+13) /* SD_STATUS (SDC) */ 63 | #define CMD16 (16) /* SET_BLOCKLEN */ 64 | #define CMD17 (17) /* READ_SINGLE_BLOCK */ 65 | #define CMD18 (18) /* READ_MULTIPLE_BLOCK */ 66 | #define CMD23 (23) /* SET_BLOCK_COUNT (MMC) */ 67 | #define ACMD23 (0x80+23) /* SET_WR_BLK_ERASE_COUNT (SDC) */ 68 | #define CMD24 (24) /* WRITE_BLOCK */ 69 | #define CMD25 (25) /* WRITE_MULTIPLE_BLOCK */ 70 | #define CMD32 (32) /* ERASE_ER_BLK_START */ 71 | #define CMD33 (33) /* ERASE_ER_BLK_END */ 72 | #define CMD38 (38) /* ERASE */ 73 | #define CMD55 (55) /* APP_CMD */ 74 | #define CMD58 (58) /* READ_OCR */ 75 | 76 | 77 | static volatile DSTATUS Stat = STA_NOINIT; /* Physical drive status */ 78 | static volatile UINT Timer1, Timer2; /* 1kHz decrement timer stopped at zero (disk_timerproc()) */ 79 | 80 | static BYTE CardType; /* Card type flags */ 81 | 82 | 83 | 84 | /*-----------------------------------------------------------------------*/ 85 | /* SPI controls (Platform dependent) */ 86 | /*-----------------------------------------------------------------------*/ 87 | 88 | /* Initialize MMC interface */ 89 | static void init_spi (void) 90 | { 91 | /* CS# */ 92 | gpio_init( DEF_SPI_CSN_PIN ); 93 | gpio_set_dir( DEF_SPI_CSN_PIN, GPIO_OUT); 94 | 95 | CS_HIGH(); /* Set CS# high */ 96 | 97 | sleep_ms(10); 98 | } 99 | 100 | static BYTE rcvr_spi() { 101 | BYTE buff; 102 | ws19804_read_blocking(0xff, &buff, 1); 103 | return buff; 104 | } 105 | 106 | /* Receive multiple byte */ 107 | static void rcvr_spi_multi ( 108 | BYTE *buff, /* Pointer to data buffer */ 109 | UINT btr /* Number of bytes to receive (even number) */ 110 | ) 111 | { 112 | ws19804_read_blocking(0xff, buff, btr); 113 | } 114 | 115 | 116 | static void xmit_spi(BYTE buff) { 117 | ws19804_write_blocking(&buff, 1); 118 | } 119 | 120 | /* Send multiple byte */ 121 | static void xmit_spi_multi ( 122 | const BYTE *buff, /* Pointer to the data */ 123 | UINT btx /* Number of bytes to send (even number) */ 124 | ) 125 | { 126 | ws19804_write_blocking(buff, btx); 127 | } 128 | 129 | 130 | /*-----------------------------------------------------------------------*/ 131 | /* Wait for card ready */ 132 | /*-----------------------------------------------------------------------*/ 133 | 134 | static int wait_ready ( /* 1:Ready, 0:Timeout */ 135 | UINT wt /* Timeout [ms] */ 136 | ) 137 | { 138 | BYTE d; 139 | 140 | for (UINT i = 0; i < wt; i++) { 141 | d = rcvr_spi(); 142 | if (d == 0xff) break; 143 | sleep_ms(1); 144 | } 145 | 146 | return (d == 0xFF) ? 1 : 0; 147 | } 148 | 149 | 150 | 151 | /*-----------------------------------------------------------------------*/ 152 | /* Deselect card and release SPI */ 153 | /*-----------------------------------------------------------------------*/ 154 | 155 | static void deselect (void) 156 | { 157 | CS_HIGH(); /* Set CS# high */ 158 | xmit_spi(0xFF); /* Dummy clock (force DO hi-z for multiple slave SPI) */ 159 | 160 | } 161 | 162 | 163 | 164 | /*-----------------------------------------------------------------------*/ 165 | /* Select card and wait for ready */ 166 | /*-----------------------------------------------------------------------*/ 167 | 168 | static int select (void) /* 1:OK, 0:Timeout */ 169 | { 170 | CS_LOW(); /* Set CS# low */ 171 | xmit_spi(0xFF); /* Dummy clock (force DO enabled) */ 172 | if (wait_ready(500)) return 1; /* Wait for card ready */ 173 | 174 | deselect(); 175 | return 0; /* Timeout */ 176 | } 177 | 178 | 179 | 180 | /*-----------------------------------------------------------------------*/ 181 | /* Receive a data packet from the MMC */ 182 | /*-----------------------------------------------------------------------*/ 183 | 184 | static int rcvr_datablock ( /* 1:OK, 0:Error */ 185 | BYTE *buff, /* Data buffer */ 186 | UINT btr /* Data block length (byte) */ 187 | ) 188 | { 189 | BYTE token; 190 | 191 | 192 | Timer1 = 200; 193 | do { /* Wait for DataStart token in timeout of 200ms */ 194 | token = rcvr_spi(); 195 | /* This loop will take a time. Insert rot_rdq() here for multitask envilonment. */ 196 | } while ((token == 0xFF) && Timer1); 197 | if(token != 0xFE) return 0; /* Function fails if invalid DataStart token or timeout */ 198 | 199 | rcvr_spi_multi(buff, btr); /* Store trailing data to the buffer */ 200 | xmit_spi(0xFF); xmit_spi(0xFF); /* Discard CRC */ 201 | 202 | return 1; /* Function succeeded */ 203 | } 204 | 205 | 206 | 207 | /*-----------------------------------------------------------------------*/ 208 | /* Send a data packet to the MMC */ 209 | /*-----------------------------------------------------------------------*/ 210 | 211 | #if FF_FS_READONLY == 0 212 | static int xmit_datablock ( /* 1:OK, 0:Failed */ 213 | const BYTE *buff, /* Ponter to 512 byte data to be sent */ 214 | BYTE token /* Token */ 215 | ) 216 | { 217 | BYTE resp; 218 | 219 | 220 | if (!wait_ready(500)) return 0; /* Wait for card ready */ 221 | 222 | xmit_spi(token); /* Send token */ 223 | if (token != 0xFD) { /* Send data if token is other than StopTran */ 224 | xmit_spi_multi(buff, 512); /* Data */ 225 | xmit_spi(0xFF); xmit_spi(0xFF); /* Dummy CRC */ 226 | 227 | resp = rcvr_spi(); /* Receive data resp */ 228 | if ((resp & 0x1F) != 0x05) return 0; /* Function fails if the data packet was not accepted */ 229 | } 230 | return 1; 231 | } 232 | #endif 233 | 234 | 235 | /*-----------------------------------------------------------------------*/ 236 | /* Send a command packet to the MMC */ 237 | /*-----------------------------------------------------------------------*/ 238 | uint8_t sd_resp[20]; 239 | static BYTE send_cmd ( /* Return value: R1 resp (bit7==1:Failed to send) */ 240 | BYTE cmd, /* Command index */ 241 | DWORD arg /* Argument */ 242 | ) 243 | { 244 | BYTE n, res; 245 | 246 | 247 | if (cmd & 0x80) { /* Send a CMD55 prior to ACMD */ 248 | cmd &= 0x7F; 249 | res = send_cmd(CMD55, 0); 250 | if (res > 1) return res; 251 | } 252 | 253 | /* Select the card and wait for ready except to stop multiple block read */ 254 | if (cmd != CMD12) { 255 | deselect(); 256 | if (!select()) return 0xFF; 257 | } 258 | 259 | /* Send command packet */ 260 | xmit_spi(0x40 | cmd); /* Start + command index */ 261 | xmit_spi((BYTE)(arg >> 24)); /* Argument[31..24] */ 262 | xmit_spi((BYTE)(arg >> 16)); /* Argument[23..16] */ 263 | xmit_spi((BYTE)(arg >> 8)); /* Argument[15..8] */ 264 | xmit_spi((BYTE)arg); /* Argument[7..0] */ 265 | n = 0x01; /* Dummy CRC + Stop */ 266 | if (cmd == CMD0) n = 0x95; /* Valid CRC for CMD0(0) */ 267 | if (cmd == CMD8) n = 0x87; /* Valid CRC for CMD8(0x1AA) */ 268 | xmit_spi(n); 269 | 270 | /* Receive command resp */ 271 | if (cmd == CMD12) xmit_spi(0xFF); /* Diacard following one byte when CMD12 */ 272 | n = 10; /* Wait for response (10 bytes max) */ 273 | do { 274 | res = rcvr_spi(); 275 | } while ((res & 0x80) && --n); 276 | 277 | return res; /* Return received response */ 278 | } 279 | 280 | 281 | 282 | /*-------------------------------------------------------------------------- 283 | 284 | Public Functions 285 | 286 | ---------------------------------------------------------------------------*/ 287 | 288 | 289 | /*-----------------------------------------------------------------------*/ 290 | /* Initialize disk drive */ 291 | /*-----------------------------------------------------------------------*/ 292 | 293 | DSTATUS disk_initialize ( 294 | BYTE drv /* Physical drive number (0) */ 295 | ) 296 | { 297 | BYTE n, cmd, ty, ocr[4]; 298 | 299 | 300 | if (drv) return STA_NOINIT; /* Supports only drive 0 */ 301 | init_spi(); /* Initialize SPI */ 302 | 303 | if (Stat & STA_NODISK) return Stat; /* Is card existing in the soket? */ 304 | 305 | FCLK_SLOW(); 306 | for (n = 10; n; n--) xmit_spi(0xFF); /* Send 80 dummy clocks */ 307 | 308 | ty = 0; 309 | if (send_cmd(CMD0, 0) == 1) { /* Put the card SPI/Idle state */ 310 | Timer1 = 1000; /* Initialization timeout = 1 sec */ 311 | if (send_cmd(CMD8, 0x1AA) == 1) { /* SDv2? */ 312 | for (n = 0; n < 4; n++) ocr[n] = rcvr_spi(); /* Get 32 bit return value of R7 resp */ 313 | if (ocr[2] == 0x01 && ocr[3] == 0xAA) { /* Is the card supports vcc of 2.7-3.6V? */ 314 | while (Timer1 && send_cmd(ACMD41, 1UL << 30)) ; /* Wait for end of initialization with ACMD41(HCS) */ 315 | if (Timer1 && send_cmd(CMD58, 0) == 0) { /* Check CCS bit in the OCR */ 316 | for (n = 0; n < 4; n++) ocr[n] = rcvr_spi(); 317 | ty = (ocr[0] & 0x40) ? CT_SDC2 | CT_BLOCK : CT_SDC2; /* Card id SDv2 */ 318 | } 319 | } 320 | } else { /* Not SDv2 card */ 321 | if (send_cmd(ACMD41, 0) <= 1) { /* SDv1 or MMC? */ 322 | ty = CT_SDC1; cmd = ACMD41; /* SDv1 (ACMD41(0)) */ 323 | } else { 324 | ty = CT_MMC3; cmd = CMD1; /* MMCv3 (CMD1(0)) */ 325 | } 326 | while (Timer1 && send_cmd(cmd, 0)) ; /* Wait for end of initialization */ 327 | if (!Timer1 || send_cmd(CMD16, 512) != 0) /* Set block length: 512 */ 328 | ty = 0; 329 | } 330 | } 331 | CardType = ty; /* Card type */ 332 | deselect(); 333 | 334 | if (ty) { /* OK */ 335 | FCLK_FAST(); /* Set fast clock */ 336 | Stat &= ~STA_NOINIT; /* Clear STA_NOINIT flag */ 337 | } else { /* Failed */ 338 | Stat = STA_NOINIT; 339 | } 340 | 341 | return Stat; 342 | } 343 | 344 | 345 | 346 | /*-----------------------------------------------------------------------*/ 347 | /* Get disk status */ 348 | /*-----------------------------------------------------------------------*/ 349 | 350 | DSTATUS disk_status ( 351 | BYTE drv /* Physical drive number (0) */ 352 | ) 353 | { 354 | if (drv) return STA_NOINIT; /* Supports only drive 0 */ 355 | 356 | return Stat; /* Return disk status */ 357 | } 358 | 359 | 360 | 361 | /*-----------------------------------------------------------------------*/ 362 | /* Read sector(s) */ 363 | /*-----------------------------------------------------------------------*/ 364 | 365 | DRESULT disk_read ( 366 | BYTE drv, /* Physical drive number (0) */ 367 | BYTE *buff, /* Pointer to the data buffer to store read data */ 368 | LBA_t sector, /* Start sector number (LBA) */ 369 | UINT count /* Number of sectors to read (1..128) */ 370 | ) 371 | { 372 | DWORD sect = (DWORD)sector; 373 | 374 | 375 | if (drv || !count) return RES_PARERR; /* Check parameter */ 376 | if (Stat & STA_NOINIT) return RES_NOTRDY; /* Check if drive is ready */ 377 | 378 | if (!(CardType & CT_BLOCK)) sect *= 512; /* LBA ot BA conversion (byte addressing cards) */ 379 | 380 | if (count == 1) { /* Single sector read */ 381 | if ((send_cmd(CMD17, sect) == 0) /* READ_SINGLE_BLOCK */ 382 | && rcvr_datablock(buff, 512)) { 383 | count = 0; 384 | } 385 | } 386 | else { /* Multiple sector read */ 387 | if (send_cmd(CMD18, sect) == 0) { /* READ_MULTIPLE_BLOCK */ 388 | do { 389 | if (!rcvr_datablock(buff, 512)) break; 390 | buff += 512; 391 | } while (--count); 392 | send_cmd(CMD12, 0); /* STOP_TRANSMISSION */ 393 | } 394 | } 395 | deselect(); 396 | 397 | return count ? RES_ERROR : RES_OK; /* Return result */ 398 | } 399 | 400 | 401 | 402 | /*-----------------------------------------------------------------------*/ 403 | /* Write sector(s) */ 404 | /*-----------------------------------------------------------------------*/ 405 | 406 | #if FF_FS_READONLY == 0 407 | DRESULT disk_write ( 408 | BYTE drv, /* Physical drive number (0) */ 409 | const BYTE *buff, /* Ponter to the data to write */ 410 | LBA_t sector, /* Start sector number (LBA) */ 411 | UINT count /* Number of sectors to write (1..128) */ 412 | ) 413 | { 414 | DWORD sect = (DWORD)sector; 415 | 416 | 417 | if (drv || !count) return RES_PARERR; /* Check parameter */ 418 | if (Stat & STA_NOINIT) return RES_NOTRDY; /* Check drive status */ 419 | if (Stat & STA_PROTECT) return RES_WRPRT; /* Check write protect */ 420 | 421 | if (!(CardType & CT_BLOCK)) sect *= 512; /* LBA ==> BA conversion (byte addressing cards) */ 422 | 423 | if (count == 1) { /* Single sector write */ 424 | if ((send_cmd(CMD24, sect) == 0) /* WRITE_BLOCK */ 425 | && xmit_datablock(buff, 0xFE)) { 426 | count = 0; 427 | } 428 | } 429 | else { /* Multiple sector write */ 430 | if (CardType & CT_SDC) send_cmd(ACMD23, count); /* Predefine number of sectors */ 431 | if (send_cmd(CMD25, sect) == 0) { /* WRITE_MULTIPLE_BLOCK */ 432 | do { 433 | if (!xmit_datablock(buff, 0xFC)) break; 434 | buff += 512; 435 | } while (--count); 436 | if (!xmit_datablock(0, 0xFD)) count = 1; /* STOP_TRAN token */ 437 | } 438 | } 439 | deselect(); 440 | 441 | return count ? RES_ERROR : RES_OK; /* Return result */ 442 | } 443 | #endif 444 | 445 | 446 | /*-----------------------------------------------------------------------*/ 447 | /* Miscellaneous drive controls other than data read/write */ 448 | /*-----------------------------------------------------------------------*/ 449 | 450 | DRESULT disk_ioctl ( 451 | BYTE drv, /* Physical drive number (0) */ 452 | BYTE cmd, /* Control command code */ 453 | void *buff /* Pointer to the conrtol data */ 454 | ) 455 | { 456 | DRESULT res; 457 | BYTE n, csd[16]; 458 | DWORD st, ed, csize; 459 | LBA_t *dp; 460 | 461 | 462 | if (drv) return RES_PARERR; /* Check parameter */ 463 | if (Stat & STA_NOINIT) return RES_NOTRDY; /* Check if drive is ready */ 464 | 465 | res = RES_ERROR; 466 | 467 | switch (cmd) { 468 | case CTRL_SYNC : /* Wait for end of internal write process of the drive */ 469 | if (select()) res = RES_OK; 470 | break; 471 | 472 | case GET_SECTOR_COUNT : /* Get drive capacity in unit of sector (DWORD) */ 473 | if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) { 474 | if ((csd[0] >> 6) == 1) { /* SDC CSD ver 2 */ 475 | csize = csd[9] + ((WORD)csd[8] << 8) + ((DWORD)(csd[7] & 63) << 16) + 1; 476 | *(LBA_t*)buff = csize << 10; 477 | } else { /* SDC CSD ver 1 or MMC */ 478 | n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2; 479 | csize = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1; 480 | *(LBA_t*)buff = csize << (n - 9); 481 | } 482 | res = RES_OK; 483 | } 484 | break; 485 | 486 | case GET_BLOCK_SIZE : /* Get erase block size in unit of sector (DWORD) */ 487 | if (CardType & CT_SDC2) { /* SDC ver 2+ */ 488 | if (send_cmd(ACMD13, 0) == 0) { /* Read SD status */ 489 | xmit_spi(0xFF); 490 | if (rcvr_datablock(csd, 16)) { /* Read partial block */ 491 | for (n = 64 - 16; n; n--) xmit_spi(0xFF); /* Purge trailing data */ 492 | *(DWORD*)buff = 16UL << (csd[10] >> 4); 493 | res = RES_OK; 494 | } 495 | } 496 | } else { /* SDC ver 1 or MMC */ 497 | if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) { /* Read CSD */ 498 | if (CardType & CT_SDC1) { /* SDC ver 1.XX */ 499 | *(DWORD*)buff = (((csd[10] & 63) << 1) + ((WORD)(csd[11] & 128) >> 7) + 1) << ((csd[13] >> 6) - 1); 500 | } else { /* MMC */ 501 | *(DWORD*)buff = ((WORD)((csd[10] & 124) >> 2) + 1) * (((csd[11] & 3) << 3) + ((csd[11] & 224) >> 5) + 1); 502 | } 503 | res = RES_OK; 504 | } 505 | } 506 | break; 507 | 508 | case CTRL_TRIM : /* Erase a block of sectors (used when _USE_ERASE == 1) */ 509 | if (!(CardType & CT_SDC)) break; /* Check if the card is SDC */ 510 | if (disk_ioctl(drv, MMC_GET_CSD, csd)) break; /* Get CSD */ 511 | if (!(csd[10] & 0x40)) break; /* Check if ERASE_BLK_EN = 1 */ 512 | dp = buff; st = (DWORD)dp[0]; ed = (DWORD)dp[1]; /* Load sector block */ 513 | if (!(CardType & CT_BLOCK)) { 514 | st *= 512; ed *= 512; 515 | } 516 | if (send_cmd(CMD32, st) == 0 && send_cmd(CMD33, ed) == 0 && send_cmd(CMD38, 0) == 0 && wait_ready(30000)) { /* Erase sector block */ 517 | res = RES_OK; /* FatFs does not check result of this command */ 518 | } 519 | break; 520 | 521 | /* Following commands are never used by FatFs module */ 522 | 523 | case MMC_GET_TYPE: /* Get MMC/SDC type (BYTE) */ 524 | *(BYTE*)buff = CardType; 525 | res = RES_OK; 526 | break; 527 | 528 | case MMC_GET_CSD: /* Read CSD (16 bytes) */ 529 | if (send_cmd(CMD9, 0) == 0 && rcvr_datablock((BYTE*)buff, 16)) { /* READ_CSD */ 530 | res = RES_OK; 531 | } 532 | break; 533 | 534 | case MMC_GET_CID: /* Read CID (16 bytes) */ 535 | if (send_cmd(CMD10, 0) == 0 && rcvr_datablock((BYTE*)buff, 16)) { /* READ_CID */ 536 | res = RES_OK; 537 | } 538 | break; 539 | 540 | case MMC_GET_OCR: /* Read OCR (4 bytes) */ 541 | if (send_cmd(CMD58, 0) == 0) { /* READ_OCR */ 542 | for (n = 0; n < 4; n++) *(((BYTE*)buff) + n) = rcvr_spi(); 543 | res = RES_OK; 544 | } 545 | break; 546 | 547 | case MMC_GET_SDSTAT: /* Read SD status (64 bytes) */ 548 | if (send_cmd(ACMD13, 0) == 0) { /* SD_STATUS */ 549 | xmit_spi(0xFF); 550 | if (rcvr_datablock((BYTE*)buff, 64)) res = RES_OK; 551 | } 552 | break; 553 | 554 | default: 555 | res = RES_PARERR; 556 | } 557 | 558 | deselect(); 559 | 560 | return res; 561 | } 562 | 563 | 564 | 565 | /*-----------------------------------------------------------------------*/ 566 | /* Device timer function */ 567 | /*-----------------------------------------------------------------------*/ 568 | /* This function must be called from timer interrupt routine in period 569 | / of 1 ms to generate card control timing. 570 | */ 571 | 572 | void disk_timerproc (void) 573 | { 574 | WORD n; 575 | BYTE s; 576 | 577 | 578 | n = Timer1; /* 1kHz decrement timer stopped at 0 */ 579 | if (n) Timer1 = --n; 580 | n = Timer2; 581 | if (n) Timer2 = --n; 582 | 583 | s = Stat; 584 | if (MMC_WP) { /* Write protected */ 585 | s |= STA_PROTECT; 586 | } else { /* Write enabled */ 587 | s &= ~STA_PROTECT; 588 | } 589 | if (MMC_CD) { /* Card is in socket */ 590 | s &= ~STA_NODISK; 591 | } else { /* Socket empty */ 592 | s |= (STA_NODISK | STA_NOINIT); 593 | } 594 | Stat = s; 595 | } 596 | 597 | --------------------------------------------------------------------------------