mirror of
https://github.com/fruit-bat/pico-zxspectrum.git
synced 2025-04-19 00:04:01 +03:00
Try Pimoroni fatfs
This commit is contained in:
parent
620d694802
commit
7f61c87e61
@ -23,9 +23,13 @@ include_directories(
|
||||
add_subdirectory(${PICO_FOLDER}/PicoDVI/software/libdvi "${CMAKE_CURRENT_BINARY_DIR}/libdvi")
|
||||
add_subdirectory(${PICO_FOLDER}/PicoDVI/software/libsprite "${CMAKE_CURRENT_BINARY_DIR}/libsprite")
|
||||
add_subdirectory(${PICO_FOLDER}/pico-vga-332/ "${CMAKE_CURRENT_BINARY_DIR}/pico-vga-332")
|
||||
add_subdirectory(${PICO_FOLDER}/no-OS-FatFS-SD-SPI-RPi-Pico/FatFs_SPI "${CMAKE_CURRENT_BINARY_DIR}/FatFs_SPI")
|
||||
add_subdirectory(${PICO_FOLDER}/pico-dvi-menu "${CMAKE_CURRENT_BINARY_DIR}/pico-dvi-menu")
|
||||
add_subdirectory(${PICO_FOLDER}/pico-emu-utils "${CMAKE_CURRENT_BINARY_DIR}/pico-emu-utils")
|
||||
|
||||
add_subdirectory(${PICO_FOLDER}/pimoroni-pico/drivers/fatfs "${CMAKE_CURRENT_BINARY_DIR}/pimoroni-pico/drivers/fatfs")
|
||||
add_subdirectory(${PICO_FOLDER}/pimoroni-pico/drivers/sdcard "${CMAKE_CURRENT_BINARY_DIR}/pimoroni-pico/drivers/sdcard")
|
||||
include_directories(${PICO_FOLDER}/pimoroni-pico/drivers/fatfs)
|
||||
include_directories(${PICO_FOLDER}/pimoroni-pico/drivers/sdcard)
|
||||
|
||||
add_subdirectory(src "${CMAKE_CURRENT_BINARY_DIR}/bin")
|
||||
|
||||
|
11
README.md
11
README.md
@ -25,14 +25,20 @@ Uses [Wren's Amazing PicoDVI](https://github.com/Wren6991/PicoDVI) and [CarlK's
|
||||
* [RetroVGA](https://hackaday.io/project/183398-retrovga-raspbery-pico-multi-retro-computer)
|
||||
* PicomputerMax
|
||||
* PicomputerZX
|
||||
* Pimoroni Pico DV Demo Base
|
||||
|
||||
<img src="docs/breadboard.png" width="200"/><a href="https://hackaday.io/project/183398-retrovga-raspbery-pico-multi-retro-computer">
|
||||
<img src="docs/retrovga.png" width="200"/><img src="docs/picomputermax.png" width="200"/><img src="docs/picomputerzx.png" width="200"/>
|
||||
<img src="docs/breadboard.png" width="200"/>
|
||||
<a href="https://hackaday.io/project/183398-retrovga-raspbery-pico-multi-retro-computer"><img src="docs/retrovga.png" width="200"/></a>
|
||||
<a href="https://hackaday.io/project/183398-retrovga-raspbery-pico-multi-retro-computer"><img src="docs/picomputermax.png" width="200"/></a>
|
||||
<a href="https://hackaday.io/project/183398-retrovga-raspbery-pico-multi-retro-computer"><img src="docs/picomputerzx.png" width="200"/></a>
|
||||
<a href="https://shop.pimoroni.com/products/pimoroni-pico-dv-demo-base"><img src="docs/P1040672_192x192.webp" /></a>
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
## Updates
|
||||
* 23/07/22 - Added target for Pico DV board
|
||||
* 23/07/22 - Moved to Pimoroni FATFS to support Pimoroni Pico DV board
|
||||
* 10/07/22 - Added basic support for PS/2 keyboards
|
||||
* 27/06/22 - Added support for RGB222 and RGBY1111 over VGA
|
||||
* 22/06/22 - Even better sound with 4 pin audio output (HDMI version only)
|
||||
@ -379,4 +385,5 @@ tio -m ODELBS /dev/ttyUSB0
|
||||
[ST7789 LCD driver reference](docs/ST7789_Datasheet.pdf)<br/>
|
||||
[RGB for 128k ZX Spectrum](docs/128_rgb.pdf)<br/>
|
||||
[PS/2 vs HID keyboard codes](docs/ps2-hid.pdf)<br/>
|
||||
[PCM 5100A DAC](PCM510xA.pdf)<br/>
|
||||
[RP2040 Datasheet](https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf)</br>
|
||||
|
BIN
docs/P1040672_1500x1500.webp
Normal file
BIN
docs/P1040672_1500x1500.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 188 KiB |
BIN
docs/P1040672_192x192.webp
Normal file
BIN
docs/P1040672_192x192.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
BIN
docs/PCM510xA.pdf
Normal file
BIN
docs/PCM510xA.pdf
Normal file
Binary file not shown.
@ -32,10 +32,13 @@ set(zxspectrum_common_libs
|
||||
pico_stdlib
|
||||
pico_multicore
|
||||
pico_util
|
||||
FatFs_SPI
|
||||
pico_emu_utils
|
||||
sdcard
|
||||
fatfs
|
||||
hardware_dma
|
||||
)
|
||||
|
||||
add_subdirectory(breadboard_hdmi)
|
||||
add_subdirectory(picomputer)
|
||||
add_subdirectory(pico_dv)
|
||||
|
||||
|
@ -169,7 +169,7 @@ public:
|
||||
ZxSpectrumType type() { return _type; }
|
||||
inline void step()
|
||||
{
|
||||
const int c = _Z80.step() + _Z80.step();
|
||||
const int c = _Z80.step();
|
||||
const uint32_t tu32 = time_us_32() << 5;
|
||||
const uint32_t tud = tu32 - _tu32;
|
||||
_tu32 = tu32;
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "ZxSpectrumAudio.h"
|
||||
|
||||
static void init_pwm_pin(uint32_t pin) {
|
||||
#ifndef AUDIO_I2S
|
||||
static void init_pwm_pin(uint32_t pin) {
|
||||
gpio_set_function(pin, GPIO_FUNC_PWM);
|
||||
const int audio_pin_slice = pwm_gpio_to_slice_num(pin);
|
||||
pwm_config config = pwm_get_default_config();
|
||||
@ -8,8 +9,12 @@ static void init_pwm_pin(uint32_t pin) {
|
||||
pwm_config_set_wrap(&config, PWM_WRAP);
|
||||
pwm_init(audio_pin_slice, &config, true);
|
||||
}
|
||||
#endif
|
||||
|
||||
void zxSpectrumAudioInit() {
|
||||
#ifdef AUDIO_I2S
|
||||
init_is2_audio();
|
||||
#else
|
||||
#ifdef BZR_PIN
|
||||
gpio_init(BZR_PIN);
|
||||
gpio_set_dir(BZR_PIN, GPIO_OUT);
|
||||
@ -26,4 +31,5 @@ void zxSpectrumAudioInit() {
|
||||
#ifdef AY8912_C_PIN
|
||||
init_pwm_pin(AY8912_C_PIN);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
@ -3,7 +3,8 @@
|
||||
#include "ZxSpectrum.h"
|
||||
#include "hardware/gpio.h"
|
||||
#include "hardware/pwm.h"
|
||||
|
||||
//
|
||||
// AUDIO_I2S I2S audio
|
||||
//
|
||||
// SPK_PIN combined audio from AY-3-8912 and buzzer
|
||||
//
|
||||
@ -18,36 +19,49 @@
|
||||
//
|
||||
// See CMakeLists.txt files for configurations
|
||||
//
|
||||
|
||||
#ifdef BZR_PIN
|
||||
#ifdef AY8912_A_PIN
|
||||
#define PWM_WRAP (255)
|
||||
#else
|
||||
#define PWM_WRAP (255 + 255 + 255)
|
||||
#endif
|
||||
#ifdef AUDIO_I2S
|
||||
#include "pico_dv/audio_i2s.h"
|
||||
#else
|
||||
#define PWM_WRAP (255 + 255 + 255 + 255)
|
||||
#ifdef BZR_PIN
|
||||
#ifdef AY8912_A_PIN
|
||||
#define PWM_WRAP (255)
|
||||
#else
|
||||
#define PWM_WRAP (255 + 255 + 255)
|
||||
#endif
|
||||
#else
|
||||
#define PWM_WRAP (255 + 255 + 255 + 255)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void zxSpectrumAudioInit();
|
||||
|
||||
inline void zxSpectrumAudioToGpio(ZxSpectrum& zxSpectrum) {
|
||||
|
||||
#ifdef BZR_PIN
|
||||
gpio_put(BZR_PIN, zxSpectrum.getBuzzer());
|
||||
#ifdef SPK_PIN
|
||||
const uint32_t l = zxSpectrum.getAnalogueAudio();
|
||||
pwm_set_gpio_level(SPK_PIN, l);
|
||||
#ifdef AUDIO_I2S
|
||||
if (is2_audio_ready()) {
|
||||
uint32_t vA, vB, vC;
|
||||
zxSpectrum.vol(vA, vB, vC);
|
||||
uint32_t s = zxSpectrum.getBuzzer() ? 255 : 0;
|
||||
uint32_t l = ((((vA << 1) + vB + s) << 4) - ((255 + 255 + 255 + 255) << (4 - 1))) & 0xffff;
|
||||
uint32_t r = ((((vC << 1) + vB + s) << 4) - ((255 + 255 + 255 + 255) << (4 - 1))) & 0xffff;
|
||||
is2_audio_put((l << 16) | r);
|
||||
}
|
||||
#else
|
||||
uint32_t vA, vB, vC;
|
||||
zxSpectrum.vol(vA, vB, vC);
|
||||
pwm_set_gpio_level(AY8912_A_PIN, vA);
|
||||
pwm_set_gpio_level(AY8912_B_PIN, vB);
|
||||
pwm_set_gpio_level(AY8912_C_PIN, vC);
|
||||
#endif
|
||||
#else
|
||||
const uint32_t l = zxSpectrum.getSpeaker();
|
||||
pwm_set_gpio_level(SPK_PIN, l);
|
||||
#ifdef BZR_PIN
|
||||
gpio_put(BZR_PIN, zxSpectrum.getBuzzer());
|
||||
#ifdef SPK_PIN
|
||||
const uint32_t l = zxSpectrum.getAnalogueAudio();
|
||||
pwm_set_gpio_level(SPK_PIN, l);
|
||||
#else
|
||||
uint32_t vA, vB, vC;
|
||||
zxSpectrum.vol(vA, vB, vC);
|
||||
pwm_set_gpio_level(AY8912_A_PIN, vA);
|
||||
pwm_set_gpio_level(AY8912_B_PIN, vB);
|
||||
pwm_set_gpio_level(AY8912_C_PIN, vC);
|
||||
#endif
|
||||
#else
|
||||
const uint32_t l = zxSpectrum.getSpeaker();
|
||||
pwm_set_gpio_level(SPK_PIN, l);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,6 @@ set(zxspectrum_hdmi_src
|
||||
main.cpp
|
||||
tmds_encode_zxspectrum.S
|
||||
tmds_encode_zxspectrum.h
|
||||
hw_config.c
|
||||
)
|
||||
|
||||
add_executable(ZxSpectrumBreadboardHdmi1PinAudio
|
||||
|
@ -250,15 +250,17 @@ void __not_in_flash_func(main_loop)() {
|
||||
while (1) {
|
||||
tuh_task();
|
||||
ps2kbd.tick();
|
||||
for (int i = 1; i < 100; ++i) {
|
||||
if (lastInterruptFrame != _frames) {
|
||||
lastInterruptFrame = _frames;
|
||||
zxSpectrum.interrupt();
|
||||
}
|
||||
zxSpectrum.step();
|
||||
zxSpectrumAudioToGpio(zxSpectrum);
|
||||
if (!showMenu) {
|
||||
for (int i = 1; i < 100; ++i) {
|
||||
if (lastInterruptFrame != _frames) {
|
||||
lastInterruptFrame = _frames;
|
||||
zxSpectrum.interrupt();
|
||||
}
|
||||
zxSpectrum.step();
|
||||
zxSpectrumAudioToGpio(zxSpectrum);
|
||||
}
|
||||
}
|
||||
if (showMenu && frames != _frames) {
|
||||
else (frames != _frames) {
|
||||
frames = _frames;
|
||||
picoDisplay.refresh();
|
||||
}
|
||||
|
60
src/pico_dv/CMakeLists.txt
Normal file
60
src/pico_dv/CMakeLists.txt
Normal file
@ -0,0 +1,60 @@
|
||||
set(zxspectrum_hdmi_src
|
||||
main.cpp
|
||||
tmds_encode_zxspectrum.S
|
||||
tmds_encode_zxspectrum.h
|
||||
audio_i2s.cpp
|
||||
)
|
||||
|
||||
add_executable(ZxSpectrumPicoDv
|
||||
${zxspectrum_common_src}
|
||||
${zxspectrum_hdmi_src}
|
||||
)
|
||||
|
||||
pico_generate_pio_header(ZxSpectrumPicoDv
|
||||
${CMAKE_CURRENT_LIST_DIR}/audio_i2s.pio
|
||||
)
|
||||
|
||||
target_compile_definitions(ZxSpectrumPicoDv PRIVATE
|
||||
DVI_DEFAULT_SERIAL_CONFIG=pimoroni_demo_hdmi_cfg
|
||||
DVI_VERTICAL_REPEAT=2
|
||||
DVI_N_TMDS_BUFFERS=3
|
||||
DVI_1BPP_BIT_REVERSE=1
|
||||
PCS_COLS=80
|
||||
PCS_ROWS=30
|
||||
FF_USE_FIND=1
|
||||
SDCARD_PIO=pio1
|
||||
SDCARD_PIO_SM=1
|
||||
SDCARD_PIN_SPI0_CS=22
|
||||
SDCARD_PIN_SPI0_SCK=5
|
||||
SDCARD_PIN_SPI0_MOSI=18
|
||||
SDCARD_PIN_SPI0_MISO=19
|
||||
AUDIO_I2S=1
|
||||
|
||||
# # compile time configuration of I2S
|
||||
# PICO_AUDIO_I2S_MONO_INPUT=1
|
||||
# #define for our example code
|
||||
# USE_AUDIO_I2S=1
|
||||
# PICO_AUDIO_I2S_DMA_IRQ=1
|
||||
# PICO_AUDIO_I2S_PIO=1
|
||||
# PICO_AUDIO_I2S_DATA=26
|
||||
# PICO_AUDIO_I2S_BCLK=27
|
||||
)
|
||||
|
||||
set(zxspectrum_hdmi_libs
|
||||
libdvi
|
||||
tinyusb_host
|
||||
tinyusb_board
|
||||
pico_emu_utils
|
||||
pico_dvi_menu
|
||||
# pico_audio_i2s
|
||||
)
|
||||
|
||||
target_link_libraries(ZxSpectrumPicoDv
|
||||
${zxspectrum_common_libs}
|
||||
${zxspectrum_hdmi_libs}
|
||||
)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(ZxSpectrumPicoDv)
|
||||
|
||||
|
40
src/pico_dv/audio_i2s.cpp
Normal file
40
src/pico_dv/audio_i2s.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "audio_i2s.pio.h"
|
||||
#include "hardware/pio.h"
|
||||
#include "hardware/gpio.h"
|
||||
#include "hardware/dma.h"
|
||||
#include "hardware/irq.h"
|
||||
#include "hardware/clocks.h"
|
||||
#include "audio_i2s.h"
|
||||
#include "audio_i2s.pio.h"
|
||||
|
||||
static void update_pio_frequency(uint32_t sample_freq, PIO audio_pio, uint pio_sm) {
|
||||
uint32_t system_clock_frequency = clock_get_hz(clk_sys);
|
||||
assert(system_clock_frequency < 0x40000000);
|
||||
uint32_t divider = system_clock_frequency * 4 / sample_freq; // avoid arithmetic overflow
|
||||
assert(divider < 0x1000000);
|
||||
pio_sm_set_clkdiv_int_frac(audio_pio, pio_sm, divider >> 8u, divider & 0xffu);
|
||||
}
|
||||
|
||||
void init_is2_audio() {
|
||||
|
||||
PIO pio = pio1;
|
||||
uint sm = 2;
|
||||
uint data_pin = 26;
|
||||
uint clock_pin_base = 27;
|
||||
|
||||
gpio_set_function(data_pin, GPIO_FUNC_PIO1);
|
||||
gpio_set_function(clock_pin_base, GPIO_FUNC_PIO1);
|
||||
gpio_set_function(clock_pin_base + 1, GPIO_FUNC_PIO1);
|
||||
|
||||
// get a state machine
|
||||
// sm = pio_claim_unused_sm(pio, true);
|
||||
|
||||
uint offset = pio_add_program(pio, &audio_i2s_program);
|
||||
|
||||
audio_i2s_program_init(pio, sm, offset, data_pin, clock_pin_base);
|
||||
update_pio_frequency(96000, pio, sm);
|
||||
|
||||
pio_sm_set_enabled(pio, sm, true);
|
||||
}
|
16
src/pico_dv/audio_i2s.h
Normal file
16
src/pico_dv/audio_i2s.h
Normal file
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include "hardware/pio.h"
|
||||
|
||||
void init_is2_audio();
|
||||
|
||||
inline bool is2_audio_ready() {
|
||||
PIO pio = pio1;
|
||||
uint sm = 2;
|
||||
return !pio_sm_is_tx_fifo_full(pio, sm);
|
||||
}
|
||||
|
||||
inline void is2_audio_put(uint32_t x) {
|
||||
PIO pio = pio1;
|
||||
uint sm = 2;
|
||||
*(volatile uint32_t*)&pio->txf[sm] = x;
|
||||
}
|
63
src/pico_dv/audio_i2s.pio
Normal file
63
src/pico_dv/audio_i2s.pio
Normal file
@ -0,0 +1,63 @@
|
||||
;
|
||||
; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
;
|
||||
; SPDX-License-Identifier: BSD-3-Clause
|
||||
;
|
||||
|
||||
; Transmit a mono or stereo I2S audio stream as stereo
|
||||
; This is 16 bits per sample; can be altered by modifying the "set" params,
|
||||
; or made programmable by replacing "set x" with "mov x, y" and using Y as a config register.
|
||||
;
|
||||
; Autopull must be enabled, with threshold set to 32.
|
||||
; Since I2S is MSB-first, shift direction should be to left.
|
||||
; Hence the format of the FIFO word is:
|
||||
;
|
||||
; | 31 : 16 | 15 : 0 |
|
||||
; | sample ws=0 | sample ws=1 |
|
||||
;
|
||||
; Data is output at 1 bit per clock. Use clock divider to adjust frequency.
|
||||
; Fractional divider will probably be needed to get correct bit clock period,
|
||||
; but for common syslck freqs this should still give a constant word select period.
|
||||
;
|
||||
; One output pin is used for the data output.
|
||||
; Two side-set pins are used. Bit 0 is clock, bit 1 is word select.
|
||||
|
||||
; Send 16 bit words to the PIO for mono, 32 bit words for stereo
|
||||
|
||||
.program audio_i2s
|
||||
.side_set 2
|
||||
|
||||
; /--- LRCLK
|
||||
; |/-- BCLK
|
||||
bitloop1: ; ||
|
||||
out pins, 1 side 0b10
|
||||
jmp x-- bitloop1 side 0b11
|
||||
out pins, 1 side 0b00
|
||||
set x, 14 side 0b01
|
||||
|
||||
bitloop0:
|
||||
out pins, 1 side 0b00
|
||||
jmp x-- bitloop0 side 0b01
|
||||
out pins, 1 side 0b10
|
||||
public entry_point:
|
||||
set x, 14 side 0b11
|
||||
|
||||
% c-sdk {
|
||||
|
||||
static inline void audio_i2s_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base) {
|
||||
pio_sm_config sm_config = audio_i2s_program_get_default_config(offset);
|
||||
|
||||
sm_config_set_out_pins(&sm_config, data_pin, 1);
|
||||
sm_config_set_sideset_pins(&sm_config, clock_pin_base);
|
||||
sm_config_set_out_shift(&sm_config, false, true, 32);
|
||||
|
||||
pio_sm_init(pio, sm, offset, &sm_config);
|
||||
|
||||
uint pin_mask = (1u << data_pin) | (3u << clock_pin_base);
|
||||
pio_sm_set_pindirs_with_mask(pio, sm, pin_mask, pin_mask);
|
||||
pio_sm_set_pins(pio, sm, 0); // clear pins
|
||||
|
||||
pio_sm_exec(pio, sm, pio_encode_jmp(offset + audio_i2s_offset_entry_point));
|
||||
}
|
||||
|
||||
%}
|
334
src/pico_dv/main.cpp
Normal file
334
src/pico_dv/main.cpp
Normal file
@ -0,0 +1,334 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "pico/multicore.h"
|
||||
#include "hardware/clocks.h"
|
||||
#include "hardware/irq.h"
|
||||
#include "hardware/sync.h"
|
||||
#include "hardware/gpio.h"
|
||||
#include "hardware/vreg.h"
|
||||
#include "hardware/structs/bus_ctrl.h"
|
||||
#include "hardware/structs/ssi.h"
|
||||
#include "hardware/dma.h"
|
||||
#include "hardware/uart.h"
|
||||
#include "pico/sem.h"
|
||||
#include "hardware/pwm.h"
|
||||
|
||||
extern "C" {
|
||||
#include "dvi.h"
|
||||
#include "dvi_serialiser.h"
|
||||
#include "common_dvi_pin_configs.h"
|
||||
#include "tmds_encode_zxspectrum.h"
|
||||
}
|
||||
#include "ZxSpectrumFatSpiKiosk.h"
|
||||
#include "ZxSpectrum.h"
|
||||
#include "ZxSpectrumHidKeyboard.h"
|
||||
#include "ZxSpectrumHidJoystick.h"
|
||||
|
||||
#include "bsp/board.h"
|
||||
#include "tusb.h"
|
||||
#include <pico/printf.h>
|
||||
#include "SdCardFatFsSpi.h"
|
||||
#include "QuickSave.h"
|
||||
#include "ZxSpectrumFatFsSpiFileLoop.h"
|
||||
|
||||
#include "PicoWinHidKeyboard.h"
|
||||
#include "PicoDisplay.h"
|
||||
#include "PicoCharRenderer.h"
|
||||
#include "ZxSpectrumMenu.h"
|
||||
#include "ZxSpectrumAudio.h"
|
||||
|
||||
#define UART_ID uart0
|
||||
#define BAUD_RATE 115200
|
||||
|
||||
// We are using pins 0 and 1, but see the GPIO function select table in the
|
||||
// datasheet for information on which other pins can be used.
|
||||
#define UART_TX_PIN 0
|
||||
#define UART_RX_PIN 1
|
||||
|
||||
// DVDD 1.2V (1.1V seems ok too)
|
||||
#define FRAME_HEIGHT 240
|
||||
#define VREG_VSEL VREG_VOLTAGE_1_20
|
||||
#define DVI_TIMING dvi_timing_640x480p_60hz
|
||||
|
||||
#define LED_PIN 25
|
||||
|
||||
struct dvi_inst dvi0;
|
||||
struct semaphore dvi_start_sem;
|
||||
|
||||
static SdCardFatFsSpi sdCard0(0);
|
||||
|
||||
// ZX Spectrum emulator
|
||||
static ZxSpectrumFatSpiKiosk zxSpectrumKisok(
|
||||
&sdCard0,
|
||||
"zxspectrum"
|
||||
);
|
||||
static ZxSpectrumFatFsSpiFileLoop zxSpectrumSnaps(
|
||||
&sdCard0,
|
||||
"zxspectrum/snapshots"
|
||||
);
|
||||
static ZxSpectrumFatFsSpiFileLoop zxSpectrumTapes(
|
||||
&sdCard0,
|
||||
"zxspectrum/tapes"
|
||||
);
|
||||
static QuickSave quickSave(
|
||||
&sdCard0,
|
||||
"zxspectrum/quicksaves"
|
||||
);
|
||||
static ZxSpectrumHidJoystick joystick;
|
||||
static ZxSpectrumHidKeyboard keyboard(
|
||||
&zxSpectrumSnaps,
|
||||
&zxSpectrumTapes,
|
||||
&quickSave,
|
||||
&joystick
|
||||
);
|
||||
static ZxSpectrum zxSpectrum(
|
||||
&keyboard,
|
||||
0,
|
||||
&joystick
|
||||
);
|
||||
static ZxSpectrumMenu picoRootWin(
|
||||
&sdCard0,
|
||||
&zxSpectrum,
|
||||
&quickSave
|
||||
);
|
||||
static PicoDisplay picoDisplay(
|
||||
pcw_screen(),
|
||||
&picoRootWin
|
||||
);
|
||||
static PicoWinHidKeyboard picoWinHidKeyboard(
|
||||
&picoDisplay
|
||||
);
|
||||
|
||||
static bool showMenu = true;
|
||||
static bool toggleMenu = false;
|
||||
|
||||
void print(hid_keyboard_report_t const *report) {
|
||||
printf("HID key report modifiers %2.2X report ", report->modifier);
|
||||
for(int i = 0; i < 6; ++i) printf("%2.2X", report->keycode[i]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
extern "C" void __not_in_flash_func(process_kbd_mount)(uint8_t dev_addr, uint8_t instance) {
|
||||
keyboard.mount();
|
||||
}
|
||||
|
||||
extern "C" void __not_in_flash_func(process_kbd_unmount)(uint8_t dev_addr, uint8_t instance) {
|
||||
keyboard.unmount();
|
||||
}
|
||||
|
||||
extern "C" void __not_in_flash_func(process_kbd_report)(hid_keyboard_report_t const *report, hid_keyboard_report_t const *prev_report) {
|
||||
#if 0
|
||||
// Some help debugging keyboard input/drivers
|
||||
printf("PREV ");print(prev_report);
|
||||
printf("CURR ");print(report);
|
||||
#endif
|
||||
|
||||
int r;
|
||||
if (showMenu) {
|
||||
r = picoWinHidKeyboard.processHidReport(report, prev_report);
|
||||
}
|
||||
else {
|
||||
r = keyboard.processHidReport(report, prev_report);
|
||||
}
|
||||
if (r == 1) toggleMenu = true;
|
||||
}
|
||||
|
||||
unsigned char* screenPtr;
|
||||
unsigned char* attrPtr;
|
||||
static volatile uint _frames = 0;
|
||||
|
||||
// Screen handler
|
||||
//
|
||||
// 256×192 Pixels
|
||||
// 32x24 Charaters
|
||||
//
|
||||
// 240-192 = 48 => 24 border rows top and bottom
|
||||
// 320-256 = 64 => 64 border pixels left and right
|
||||
//
|
||||
static inline void prepare_scanline(const uint y, uint f) {
|
||||
|
||||
const uint8_t borderColor = zxSpectrum.borderColour();
|
||||
uint32_t *tmdsbuf;
|
||||
queue_remove_blocking(&dvi0.q_tmds_free, &tmdsbuf);
|
||||
uint32_t *p = tmdsbuf;
|
||||
const uint v = y - 24;
|
||||
const uint8_t *s = screenPtr + ((v & 0x7) << 8) + ((v & 0x38) << 2) + ((v & 0xc0) << 5);
|
||||
const uint8_t *a = attrPtr+((v>>3)<<5);
|
||||
const int m = (f >> 5) & 1 ? 0xff : 0x7f;
|
||||
|
||||
if (y < 24 || y >= (24+192)) {
|
||||
for (int plane = 0; plane < 3; ++plane) {
|
||||
p = tmds_encode_border(
|
||||
40, // r0 is width in characters
|
||||
plane, // r1 is colour channel
|
||||
p, // r2 is output TMDS buffer
|
||||
borderColor // r3 is the colour attribute
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int plane = 0; plane < 3; ++plane) {
|
||||
p = tmds_encode_border(
|
||||
4, // r0 is width in characters
|
||||
plane, // r1 is colour channel
|
||||
p, // r2 is output TMDS buffer
|
||||
borderColor // r3 is the colour attribute
|
||||
);
|
||||
p = tmds_encode_screen(
|
||||
(const uint8_t*)s,
|
||||
a,
|
||||
p,
|
||||
m,
|
||||
plane
|
||||
);
|
||||
p = tmds_encode_border(
|
||||
4, // r0 is width in characters
|
||||
plane, // r1 is colour channel
|
||||
p, // r2 is output TMDS buffer
|
||||
borderColor // r3 is the colour attribute
|
||||
);
|
||||
}
|
||||
}
|
||||
queue_add_blocking(&dvi0.q_tmds_valid, &tmdsbuf);
|
||||
}
|
||||
|
||||
void __not_in_flash_func(core1_scanline_callback)() {
|
||||
static uint y = 1;
|
||||
static uint ys = 0;
|
||||
if (showMenu) {
|
||||
uint rs = pcw_prepare_scanline_80(&dvi0, y++, ys, _frames);
|
||||
if (0 == (y & 7)) {
|
||||
ys += rs;
|
||||
}
|
||||
}
|
||||
else {
|
||||
prepare_scanline(y++, _frames);
|
||||
}
|
||||
if (y == FRAME_HEIGHT) {
|
||||
_frames++;
|
||||
y = 0;
|
||||
ys = 0;
|
||||
|
||||
// TODO Tidy this mechanism up
|
||||
screenPtr = zxSpectrum.screenPtr();
|
||||
attrPtr = screenPtr + (32 * 24 * 8);
|
||||
|
||||
if (toggleMenu) {
|
||||
showMenu = !showMenu;
|
||||
toggleMenu = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void __not_in_flash_func(core1_main)() {
|
||||
dvi_register_irqs_this_core(&dvi0, DMA_IRQ_1);
|
||||
sem_acquire_blocking(&dvi_start_sem);
|
||||
|
||||
dvi_start(&dvi0);
|
||||
|
||||
// The text display is completely IRQ driven (takes up around 30% of cycles @
|
||||
// VGA). We could do something useful, or we could just take a nice nap
|
||||
while (1)
|
||||
__wfi();
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
void __not_in_flash_func(main_loop)() {
|
||||
|
||||
unsigned int lastInterruptFrame = _frames;
|
||||
|
||||
uint frames = 0;
|
||||
|
||||
while (1) {
|
||||
tuh_task();
|
||||
if (!showMenu) {
|
||||
for (int i = 1; i < 100; ++i) {
|
||||
if (lastInterruptFrame != _frames) {
|
||||
lastInterruptFrame = _frames;
|
||||
zxSpectrum.interrupt();
|
||||
}
|
||||
zxSpectrum.step();
|
||||
zxSpectrumAudioToGpio(zxSpectrum);
|
||||
}
|
||||
}
|
||||
else if (frames != _frames) {
|
||||
frames = _frames;
|
||||
picoDisplay.refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
vreg_set_voltage(VREG_VSEL);
|
||||
sleep_ms(10);
|
||||
#ifdef RUN_FROM_CRYSTAL
|
||||
set_sys_clock_khz(12000, true);
|
||||
#else
|
||||
// Run system at TMDS bit clock
|
||||
set_sys_clock_khz(DVI_TIMING.bit_clk_khz, true);
|
||||
#endif
|
||||
|
||||
setup_default_uart();
|
||||
tusb_init();
|
||||
|
||||
gpio_init(LED_PIN);
|
||||
gpio_set_dir(LED_PIN, GPIO_OUT);
|
||||
|
||||
// Configure the GPIO pins for audio
|
||||
zxSpectrumAudioInit();
|
||||
|
||||
screenPtr = zxSpectrum.screenPtr();
|
||||
attrPtr = screenPtr + (32 * 24 * 8);
|
||||
|
||||
keyboard.setZxSpectrum(&zxSpectrum);
|
||||
|
||||
// Initialise the menu renderer
|
||||
pcw_init_renderer();
|
||||
|
||||
printf("Configuring DVI\n");
|
||||
dvi0.timing = &DVI_TIMING;
|
||||
dvi0.ser_cfg = DVI_DEFAULT_SERIAL_CONFIG;
|
||||
dvi0.scanline_callback = core1_scanline_callback;
|
||||
dvi_init(&dvi0, next_striped_spin_lock_num(), next_striped_spin_lock_num());
|
||||
|
||||
printf("Prepare first scanline\n");
|
||||
|
||||
prepare_scanline(0, 0);
|
||||
|
||||
printf("Core 1 start\n");
|
||||
sem_init(&dvi_start_sem, 0, 1);
|
||||
hw_set_bits(&bus_ctrl_hw->priority, BUSCTRL_BUS_PRIORITY_PROC1_BITS);
|
||||
|
||||
multicore_launch_core1(core1_main);
|
||||
|
||||
picoRootWin.showMessage([=](PicoPen *pen) {
|
||||
pen->printAtF(3, 1, false, "Reading from SD card...");
|
||||
});
|
||||
|
||||
picoDisplay.refresh();
|
||||
|
||||
sem_release(&dvi_start_sem);
|
||||
|
||||
if (sdCard0.mount()) {
|
||||
|
||||
// Set up the quick load loops
|
||||
zxSpectrumSnaps.reload();
|
||||
zxSpectrumTapes.reload();
|
||||
|
||||
// Load quick save slot 1 if present
|
||||
if (quickSave.used(0)) {
|
||||
quickSave.load(&zxSpectrum, 0);
|
||||
}
|
||||
|
||||
bool isKiosk = zxSpectrumKisok.isKiosk();
|
||||
keyboard.setKiosk(isKiosk);
|
||||
}
|
||||
|
||||
showMenu = false;
|
||||
picoRootWin.removeMessage();
|
||||
|
||||
main_loop();
|
||||
|
||||
__builtin_unreachable();
|
||||
}
|
1000
src/pico_dv/tmds_encode_zxspectrum.S
Normal file
1000
src/pico_dv/tmds_encode_zxspectrum.S
Normal file
File diff suppressed because it is too large
Load Diff
22
src/pico_dv/tmds_encode_zxspectrum.h
Normal file
22
src/pico_dv/tmds_encode_zxspectrum.h
Normal file
@ -0,0 +1,22 @@
|
||||
#ifndef _TMDS_ENCODE_ZXSPECTRUM_H
|
||||
#define _TMDS_ENCODE_ZXSPECTRUM_H
|
||||
|
||||
#include "pico/types.h"
|
||||
|
||||
uint32_t * __not_in_flash_func(tmds_encode_screen)(
|
||||
const uint8_t *screenRowPtr,
|
||||
const uint8_t *attributeRowPtr,
|
||||
uint32_t *tmdsbuf,
|
||||
uint32_t flash,
|
||||
uint32_t plane
|
||||
);
|
||||
|
||||
|
||||
uint32_t *__not_in_flash_func(tmds_encode_border)(
|
||||
uint32_t nchars, // r0 is width in characters
|
||||
uint32_t plane, // r1 is colour channel
|
||||
uint32_t *tmdsbuf, // r2 is output TMDS buffer
|
||||
uint32_t attr // r3 is the colour attribute
|
||||
);
|
||||
|
||||
#endif
|
@ -4,7 +4,6 @@ set(picomputer_common_src
|
||||
${CMAKE_CURRENT_LIST_DIR}/pzx_keyscan.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/pzx_keyscan.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/ZxSpectrumPicomputerJoystick.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/hw_config.c
|
||||
)
|
||||
|
||||
add_subdirectory(picomputer_vga)
|
||||
|
@ -207,17 +207,17 @@ void __not_in_flash_func(main_loop)() {
|
||||
pzx_keyscan_get_hid_reports(&curr, &prev);
|
||||
process_picomputer_kbd_report(curr, prev);
|
||||
}
|
||||
|
||||
for (int i = 1; i < 100; ++i) {
|
||||
if (lastInterruptFrame != _frames) {
|
||||
lastInterruptFrame = _frames;
|
||||
zxSpectrum.interrupt();
|
||||
if (!showMenu) {
|
||||
for (int i = 1; i < 100; ++i) {
|
||||
if (lastInterruptFrame != _frames) {
|
||||
lastInterruptFrame = _frames;
|
||||
zxSpectrum.interrupt();
|
||||
}
|
||||
zxSpectrum.step();
|
||||
zxSpectrumAudioToGpio(zxSpectrum);
|
||||
}
|
||||
zxSpectrum.step();
|
||||
zxSpectrumAudioToGpio(zxSpectrum);
|
||||
}
|
||||
|
||||
if (showMenu && frames != _frames) {
|
||||
else (frames != _frames) {
|
||||
frames = _frames;
|
||||
picoDisplay.refresh();
|
||||
}
|
||||
|
@ -200,16 +200,17 @@ void __not_in_flash_func(main_loop)(){
|
||||
pzx_keyscan_get_hid_reports(&curr, &prev);
|
||||
process_picomputer_kbd_report(curr, prev);
|
||||
|
||||
for (int i = 1; i < 100; ++i) {
|
||||
if (lastInterruptFrame != _frames) {
|
||||
lastInterruptFrame = _frames;
|
||||
zxSpectrum.interrupt();
|
||||
if (!showMenu) {
|
||||
for (int i = 1; i < 100; ++i) {
|
||||
if (lastInterruptFrame != _frames) {
|
||||
lastInterruptFrame = _frames;
|
||||
zxSpectrum.interrupt();
|
||||
}
|
||||
zxSpectrum.step();
|
||||
zxSpectrumAudioToGpio(zxSpectrum);
|
||||
}
|
||||
zxSpectrum.step();
|
||||
zxSpectrumAudioToGpio(zxSpectrum);
|
||||
}
|
||||
|
||||
if (showMenu && frames != _frames) {
|
||||
else (frames != _frames) {
|
||||
frames = _frames;
|
||||
picoDisplay.refresh();
|
||||
}
|
||||
|
@ -200,16 +200,17 @@ void __not_in_flash_func(main_loop)(){
|
||||
pzx_keyscan_get_hid_reports(&curr, &prev);
|
||||
process_picomputer_kbd_report(curr, prev);
|
||||
|
||||
for (int i = 1; i < 100; ++i) {
|
||||
if (lastInterruptFrame != _frames) {
|
||||
lastInterruptFrame = _frames;
|
||||
zxSpectrum.interrupt();
|
||||
if (!showMenu) {
|
||||
for (int i = 1; i < 100; ++i) {
|
||||
if (lastInterruptFrame != _frames) {
|
||||
lastInterruptFrame = _frames;
|
||||
zxSpectrum.interrupt();
|
||||
}
|
||||
zxSpectrum.step();
|
||||
zxSpectrumAudioToGpio(zxSpectrum);
|
||||
}
|
||||
zxSpectrum.step();
|
||||
zxSpectrumAudioToGpio(zxSpectrum);
|
||||
}
|
||||
|
||||
if (showMenu && frames != _frames) {
|
||||
else (frames != _frames) {
|
||||
frames = _frames;
|
||||
picoDisplay.refresh();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user