rp2350 support and a minor fix
This commit is contained in:
parent
836a29586f
commit
29a453c980
11 changed files with 91 additions and 18 deletions
11
README.md
11
README.md
|
|
@ -1,13 +1,13 @@
|
||||||
# RP2040 Doom
|
# RP2040 (+RP2350) Doom
|
||||||
|
|
||||||
This is a port of Doom for RP2040 devices, derived from [Chocolate Doom](https://github.com/chocolate-doom/chocolate-doom).
|
This is a port of Doom for RP2040 / RP2350 devices, derived from [Chocolate Doom](https://github.com/chocolate-doom/chocolate-doom).
|
||||||
|
|
||||||
Significant changes have been made to support running on the RP2040 device, but particularly to support running the
|
Significant changes have been made to support running on the RP2xxx device, but particularly to support running the
|
||||||
entire shareware `DOOM1.WAD` which is 4M big on a Raspberry Pi Pico with only 2M flash!
|
entire shareware `DOOM1.WAD` which is 4M big on a Raspberry Pi Pico with only 2M flash!
|
||||||
|
|
||||||
You can read many details on this port in the blog post [here](https://kilograham.github.io/rp2040-doom/).
|
You can read many details on this port in the blog post [here](https://kilograham.github.io/rp2040-doom/).
|
||||||
|
|
||||||
Note that a hopefully-fully-functional `chocolate-doom` executable is buildable from this RP2040 code base as a
|
Note that a hopefully-fully-functional `chocolate-doom` executable is buildable from this RP2xxx code base as a
|
||||||
means of
|
means of
|
||||||
verification that everything still works, but whilst they can still be built, Hexen, Strife and Heretic are almost
|
verification that everything still works, but whilst they can still be built, Hexen, Strife and Heretic are almost
|
||||||
certainly broken, so are not built by default.
|
certainly broken, so are not built by default.
|
||||||
|
|
@ -101,8 +101,7 @@ pico-sdk requisites (e.g.
|
||||||
[documentation](https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf). I have been building against
|
[documentation](https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf). I have been building against
|
||||||
the `develop` branch of `pico-sdk`, so I recommend that..
|
the `develop` branch of `pico-sdk`, so I recommend that..
|
||||||
|
|
||||||
**NOTE: I was building with arm-none-eabi-gcc 9.2.1 .. it seems like other versions may cause problems with binary
|
**NOTE: I am building with arm-none-eabi-gcc 13.2.rel1 .. whilst other versions may work, changes in compiler version may affect the binary size which, being tight, can cause problems (either link failure, or you may see stack overflow in the form of color palette corruption). Particularly I know tjhat arm-none-eabi-gcc 10.x versions don't work well.**
|
||||||
size, so stick with that for now.**
|
|
||||||
|
|
||||||
For USB keyboard input support, RP2040 Doom currently uses a modified version of TinyUSB included as a submodule.
|
For USB keyboard input support, RP2040 Doom currently uses a modified version of TinyUSB included as a submodule.
|
||||||
Make sure you have initialized this submodule via `git submodule update --init`
|
Make sure you have initialized this submodule via `git submodule update --init`
|
||||||
|
|
|
||||||
|
|
@ -194,15 +194,27 @@ typedef uint8_t floor_ceiling_clip_t;
|
||||||
|
|
||||||
#if PICO_ON_DEVICE
|
#if PICO_ON_DEVICE
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#if !PICO_RP2350
|
||||||
|
#define SHORTPTR_BASE 0x20000000
|
||||||
|
#else
|
||||||
|
#define SHORTPTR_BASE 0x20030000
|
||||||
|
#endif
|
||||||
typedef uint16_t shortptr_t;
|
typedef uint16_t shortptr_t;
|
||||||
static inline void *shortptr_to_ptr(shortptr_t s) {
|
static inline void *shortptr_to_ptr(shortptr_t s) {
|
||||||
return s ? (void *)(0x20000000 + s * 4) : NULL;
|
return s ? (void *)(SHORTPTR_BASE + s * 4) : NULL;
|
||||||
}
|
}
|
||||||
static inline shortptr_t ptr_to_shortptr(void *p) {
|
static inline shortptr_t ptr_to_shortptr(void *p) {
|
||||||
if (!p) return 0;
|
if (!p) return 0;
|
||||||
uintptr_t v = (uintptr_t)p;
|
uintptr_t v = (uintptr_t)p;
|
||||||
assert(v>=0x20000004 && v <= 0x20040000 && !(v&3));
|
if (!(v>=SHORTPTR_BASE+4 && v < SHORTPTR_BASE + 0x40000 && !(v&3))) {
|
||||||
|
asm("bkpt #0");
|
||||||
|
}
|
||||||
|
assert(v>=SHORTPTR_BASE+4 && v < SHORTPTR_BASE + 0x40000 && !(v&3));
|
||||||
|
#if SHORTPTR_BASE == 0x20000000
|
||||||
return (shortptr_t) ((v << 14u)>>16u);
|
return (shortptr_t) ((v << 14u)>>16u);
|
||||||
|
#else
|
||||||
|
return (shortptr_t) ((v - SHORTPTR_BASE) >> 2);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
typedef void *shortptr_t;
|
typedef void *shortptr_t;
|
||||||
|
|
|
||||||
18
src/i_main.c
18
src/i_main.c
|
|
@ -30,6 +30,7 @@
|
||||||
#include "pico/sem.h"
|
#include "pico/sem.h"
|
||||||
#include "pico/multicore.h"
|
#include "pico/multicore.h"
|
||||||
#if PICO_ON_DEVICE
|
#if PICO_ON_DEVICE
|
||||||
|
#include "hardware/clocks.h"
|
||||||
#include "hardware/vreg.h"
|
#include "hardware/vreg.h"
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -39,7 +40,9 @@
|
||||||
#include "doomtype.h"
|
#include "doomtype.h"
|
||||||
#include "i_system.h"
|
#include "i_system.h"
|
||||||
#include "m_argv.h"
|
#include "m_argv.h"
|
||||||
|
#if PICO_RP2350
|
||||||
|
#include "hardware/structs/qmi.h"
|
||||||
|
#endif
|
||||||
//
|
//
|
||||||
// D_DoomMain()
|
// D_DoomMain()
|
||||||
// Not a globally visible function, just included for source reference,
|
// Not a globally visible function, just included for source reference,
|
||||||
|
|
@ -61,8 +64,19 @@ int main(int argc, char **argv)
|
||||||
myargv = argv;
|
myargv = argv;
|
||||||
#endif
|
#endif
|
||||||
#if PICO_ON_DEVICE
|
#if PICO_ON_DEVICE
|
||||||
|
#if PICO_RP2350
|
||||||
|
uint clkdiv = 3;
|
||||||
|
uint rxdelay = 2;
|
||||||
|
hw_write_masked(
|
||||||
|
&qmi_hw->m[0].timing,
|
||||||
|
((clkdiv << QMI_M0_TIMING_CLKDIV_LSB) & QMI_M0_TIMING_CLKDIV_BITS) |
|
||||||
|
((rxdelay << QMI_M0_TIMING_RXDELAY_LSB) & QMI_M0_TIMING_RXDELAY_BITS),
|
||||||
|
QMI_M0_TIMING_CLKDIV_BITS | QMI_M0_TIMING_RXDELAY_BITS
|
||||||
|
);
|
||||||
|
#endif
|
||||||
vreg_set_voltage(VREG_VOLTAGE_1_30);
|
vreg_set_voltage(VREG_VOLTAGE_1_30);
|
||||||
// todo pause? is this the cause of the cold start isue?
|
busy_wait_us(1000);
|
||||||
|
// todo pause? is this the cause of the cold start issue?
|
||||||
set_sys_clock_khz(270000, true);
|
set_sys_clock_khz(270000, true);
|
||||||
#if !USE_PICO_NET
|
#if !USE_PICO_NET
|
||||||
// debug ?
|
// debug ?
|
||||||
|
|
|
||||||
|
|
@ -317,7 +317,11 @@ const char *type_name(pd_column column) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !PICO_RP2350
|
||||||
#define RENDER_COL_MAX 3600
|
#define RENDER_COL_MAX 3600
|
||||||
|
#else
|
||||||
|
#define RENDER_COL_MAX 7200
|
||||||
|
#endif
|
||||||
static uint8_t __aligned(4) list_buffer[RENDER_COL_MAX * sizeof(pd_column) + 64*64]; // extra 64*64 is for one flat
|
static uint8_t __aligned(4) list_buffer[RENDER_COL_MAX * sizeof(pd_column) + 64*64]; // extra 64*64 is for one flat
|
||||||
static uint8_t *last_list_buffer_limit = list_buffer + sizeof(list_buffer);
|
static uint8_t *last_list_buffer_limit = list_buffer + sizeof(list_buffer);
|
||||||
//static_assert(text_font_cpy > list_buffer, "");
|
//static_assert(text_font_cpy > list_buffer, "");
|
||||||
|
|
@ -817,7 +821,12 @@ void pd_init() {
|
||||||
sem_init(&core1_done, 0, 1);
|
sem_init(&core1_done, 0, 1);
|
||||||
#if PICO_ON_DEVICE
|
#if PICO_ON_DEVICE
|
||||||
static_assert(sizeof(vpatchlists_t) < 0xc00, "");
|
static_assert(sizeof(vpatchlists_t) < 0xc00, "");
|
||||||
|
#if !PICO_RP2350
|
||||||
vpatchlists = (vpatchlists_t *)(USBCTRL_DPRAM_BASE + 0x400);
|
vpatchlists = (vpatchlists_t *)(USBCTRL_DPRAM_BASE + 0x400);
|
||||||
|
#else
|
||||||
|
static_assert(SRAM_SCRATCH_X_BASE - 0xc00 >= SHORTPTR_BASE + 0x4000, ""); // avoid potential heap locations
|
||||||
|
vpatchlists = (vpatchlists_t *)(SRAM_SCRATCH_X_BASE - 0xc00);
|
||||||
|
#endif
|
||||||
#else
|
#else
|
||||||
vpatchlists = (vpatchlists_t*)malloc(sizeof(vpatchlists_t));
|
vpatchlists = (vpatchlists_t*)malloc(sizeof(vpatchlists_t));
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,9 @@ target_compile_definitions(common_pico INTERFACE
|
||||||
NO_USE_MOUSE=1
|
NO_USE_MOUSE=1
|
||||||
PICO_AUDIO_I2S_PIO=1
|
PICO_AUDIO_I2S_PIO=1
|
||||||
PICO_AUDIO_I2S_DMA_IRQ=1
|
PICO_AUDIO_I2S_DMA_IRQ=1
|
||||||
)
|
PICO_USE_SW_SPIN_LOCKS=0
|
||||||
|
PICO_USE_STACK_GUARDS=0 # todo we can actually use these sensibly, but right now we do overflow!
|
||||||
|
)
|
||||||
|
|
||||||
pico_generate_pio_header(common_pico ${CMAKE_CURRENT_LIST_DIR}/video_doom.pio)
|
pico_generate_pio_header(common_pico ${CMAKE_CURRENT_LIST_DIR}/video_doom.pio)
|
||||||
target_link_libraries(common_pico INTERFACE pico_stdlib pico_multicore pico_scanvideo_dpi)
|
target_link_libraries(common_pico INTERFACE pico_stdlib pico_multicore pico_scanvideo_dpi)
|
||||||
|
|
|
||||||
|
|
@ -142,6 +142,16 @@ static byte *AutoAllocMemory(int *size, int default_ram, int min_ram)
|
||||||
extern char __end__;
|
extern char __end__;
|
||||||
zonemem = (uint8_t *)(((uintptr_t)&__end__)&~3);
|
zonemem = (uint8_t *)(((uintptr_t)&__end__)&~3);
|
||||||
*size = ((uint8_t *)SRAM4_BASE) - zonemem;
|
*size = ((uint8_t *)SRAM4_BASE) - zonemem;
|
||||||
|
#if !PICO_RP2350
|
||||||
|
*size = ((uint8_t *)SRAM4_BASE) - zonemem;
|
||||||
|
#else
|
||||||
|
// because of our shortptrs, heap cannot be bigger than 256K
|
||||||
|
// todo limit this to any static stuff at the end
|
||||||
|
*size = ((uint8_t *)SHORTPTR_BASE + 0x40000) - zonemem;
|
||||||
|
if (SHORTPTR_BASE >= (uintptr_t)zonemem) {
|
||||||
|
I_Error("SHORTPTR_BASE bad");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#else
|
#else
|
||||||
#error use zone for malloc only on device
|
#error use zone for malloc only on device
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -751,7 +751,12 @@ static inline uint draw_vpatch(uint16_t *dest, patch_t *patch, vpatchlist_t *vp,
|
||||||
#if PICO_ON_DEVICE
|
#if PICO_ON_DEVICE
|
||||||
if (patch == stbar) {
|
if (patch == stbar) {
|
||||||
static const uint8_t *cached_data;
|
static const uint8_t *cached_data;
|
||||||
|
#if PICO_RP2040
|
||||||
static uint32_t __scratch_x("data_cache") data_cache[41];
|
static uint32_t __scratch_x("data_cache") data_cache[41];
|
||||||
|
#else
|
||||||
|
// short of scratch space on RP2350 for some reason, so lets put this in main RAM
|
||||||
|
static uint32_t data_cache[41];
|
||||||
|
#endif
|
||||||
int i = 0;
|
int i = 0;
|
||||||
uint32_t *d = (uint32_t *) dest;
|
uint32_t *d = (uint32_t *) dest;
|
||||||
#define DMA_CHANNEL 11
|
#define DMA_CHANNEL 11
|
||||||
|
|
@ -772,14 +777,20 @@ static inline uint draw_vpatch(uint16_t *dest, patch_t *patch, vpatchlist_t *vp,
|
||||||
// once = true;
|
// once = true;
|
||||||
xip_ctrl_hw->stream_ctr = 0;
|
xip_ctrl_hw->stream_ctr = 0;
|
||||||
// workaround yucky bug
|
// workaround yucky bug
|
||||||
|
#if !PICO_RP2350
|
||||||
(void) *(io_rw_32 *) XIP_NOCACHE_NOALLOC_BASE;
|
(void) *(io_rw_32 *) XIP_NOCACHE_NOALLOC_BASE;
|
||||||
xip_ctrl_hw->stream_fifo;
|
xip_ctrl_hw->stream_fifo;
|
||||||
|
#endif
|
||||||
dma_channel_abort(DMA_CHANNEL);
|
dma_channel_abort(DMA_CHANNEL);
|
||||||
dma_channel_config c = dma_channel_get_default_config(DMA_CHANNEL);
|
dma_channel_config c = dma_channel_get_default_config(DMA_CHANNEL);
|
||||||
channel_config_set_read_increment(&c, false);
|
channel_config_set_read_increment(&c, false);
|
||||||
channel_config_set_write_increment(&c, true);
|
channel_config_set_write_increment(&c, true);
|
||||||
channel_config_set_dreq(&c, DREQ_XIP_STREAM);
|
channel_config_set_dreq(&c, DREQ_XIP_STREAM);
|
||||||
|
#if !PICO_RP2350
|
||||||
dma_channel_set_read_addr(DMA_CHANNEL, (void *) XIP_AUX_BASE, false);
|
dma_channel_set_read_addr(DMA_CHANNEL, (void *) XIP_AUX_BASE, false);
|
||||||
|
#else
|
||||||
|
dma_channel_set_read_addr(DMA_CHANNEL, &xip_ctrl_hw->stream_fifo, false);
|
||||||
|
#endif
|
||||||
dma_channel_set_config(DMA_CHANNEL, &c, false);
|
dma_channel_set_config(DMA_CHANNEL, &c, false);
|
||||||
cached_data = data + SCREENWIDTH / 2;
|
cached_data = data + SCREENWIDTH / 2;
|
||||||
xip_ctrl_hw->stream_addr = (uintptr_t) cached_data;
|
xip_ctrl_hw->stream_addr = (uintptr_t) cached_data;
|
||||||
|
|
@ -1067,7 +1078,11 @@ void __scratch_x("scanlines") fill_scanlines() {
|
||||||
static void __not_in_flash_func(free_buffer_callback)() {
|
static void __not_in_flash_func(free_buffer_callback)() {
|
||||||
// irq_set_pending(LOW_PRIO_IRQ);
|
// irq_set_pending(LOW_PRIO_IRQ);
|
||||||
// ^ is in flash by default
|
// ^ is in flash by default
|
||||||
|
#if !PICO_RP2350
|
||||||
*((io_rw_32 *) (PPB_BASE + M0PLUS_NVIC_ISPR_OFFSET)) = 1u << LOW_PRIO_IRQ;
|
*((io_rw_32 *) (PPB_BASE + M0PLUS_NVIC_ISPR_OFFSET)) = 1u << LOW_PRIO_IRQ;
|
||||||
|
#else
|
||||||
|
nvic_hw->ispr[LOW_PRIO_IRQ / 32] = 1 << (LOW_PRIO_IRQ % 32);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -1100,6 +1115,9 @@ static void core1() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PICO_RP2350
|
||||||
|
#include "hardware/structs/accessctrl.h"
|
||||||
|
#endif
|
||||||
void I_InitGraphics(void)
|
void I_InitGraphics(void)
|
||||||
{
|
{
|
||||||
stbar = resolve_vpatch_handle(VPATCH_STBAR);
|
stbar = resolve_vpatch_handle(VPATCH_STBAR);
|
||||||
|
|
@ -1112,6 +1130,9 @@ void I_InitGraphics(void)
|
||||||
sem_acquire_blocking(&core1_launch);
|
sem_acquire_blocking(&core1_launch);
|
||||||
#if USE_ZONE_FOR_MALLOC
|
#if USE_ZONE_FOR_MALLOC
|
||||||
disallow_core1_malloc = true;
|
disallow_core1_malloc = true;
|
||||||
|
#endif
|
||||||
|
#if PICO_RP2350
|
||||||
|
hw_set_bits(&accessctrl_hw->xip_ctrl, ACCESSCTRL_PASSWORD_BITS | 0xff);
|
||||||
#endif
|
#endif
|
||||||
initialized = true;
|
initialized = true;
|
||||||
}
|
}
|
||||||
|
|
@ -1179,6 +1200,7 @@ int I_GetPaletteIndex(int r, int g, int b)
|
||||||
|
|
||||||
#if !NO_USE_ENDDOOM
|
#if !NO_USE_ENDDOOM
|
||||||
void I_Endoom(byte *endoom_data) {
|
void I_Endoom(byte *endoom_data) {
|
||||||
|
#if SUPPORT_TEXT
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
uint8_t *wa = pd_get_work_area(&size);
|
uint8_t *wa = pd_get_work_area(&size);
|
||||||
assert(size >=TEXT_SCANLINE_BUFFER_TOTAL_WORDS * 4 + 80*25*2 + 4096);
|
assert(size >=TEXT_SCANLINE_BUFFER_TOTAL_WORDS * 4 + 80*25*2 + 4096);
|
||||||
|
|
@ -1210,6 +1232,7 @@ void I_Endoom(byte *endoom_data) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
text_screen_data = text_screen_cpy;
|
text_screen_data = text_screen_cpy;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,6 @@
|
||||||
#include "picoflash.h"
|
#include "picoflash.h"
|
||||||
#include "pico/bootrom.h"
|
#include "pico/bootrom.h"
|
||||||
|
|
||||||
#include "hardware/structs/ssi.h"
|
|
||||||
#include "hardware/structs/ioqspi.h"
|
|
||||||
|
|
||||||
#define FLASH_BLOCK_ERASE_CMD 0xd8
|
#define FLASH_BLOCK_ERASE_CMD 0xd8
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
@ -23,8 +20,13 @@
|
||||||
#define BOOT2_SIZE_WORDS 64
|
#define BOOT2_SIZE_WORDS 64
|
||||||
|
|
||||||
static void __no_inline_not_in_flash_func(flash_init_boot2_copyout)(uint32_t boot2_copyout[BOOT2_SIZE_WORDS]) {
|
static void __no_inline_not_in_flash_func(flash_init_boot2_copyout)(uint32_t boot2_copyout[BOOT2_SIZE_WORDS]) {
|
||||||
|
#if PICO_RP2040
|
||||||
|
const volatile uint32_t *copy_from = (uint32_t *)XIP_BASE;
|
||||||
|
#else
|
||||||
|
const volatile uint32_t *copy_from = (uint32_t *)BOOTRAM_BASE;
|
||||||
|
#endif
|
||||||
for (int i = 0; i < BOOT2_SIZE_WORDS; ++i)
|
for (int i = 0; i < BOOT2_SIZE_WORDS; ++i)
|
||||||
boot2_copyout[i] = ((uint32_t *)XIP_BASE)[i];
|
boot2_copyout[i] = copy_from[i];
|
||||||
__compiler_memory_barrier();
|
__compiler_memory_barrier();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -664,8 +664,9 @@ void piconet_init() {
|
||||||
gpio_pull_up(PICO_DEFAULT_I2C_SCL_PIN);
|
gpio_pull_up(PICO_DEFAULT_I2C_SCL_PIN);
|
||||||
irq_set_exclusive_handler(I2C_IRQ, i2c_irq_handler);
|
irq_set_exclusive_handler(I2C_IRQ, i2c_irq_handler);
|
||||||
hardware_alarm_set_callback(PERIODIC_ALARM_NUM, periodic_tick);
|
hardware_alarm_set_callback(PERIODIC_ALARM_NUM, periodic_tick);
|
||||||
irq_set_priority(TIMER_IRQ_0 + PERIODIC_ALARM_NUM, 0xc0); // don't want timer pre-empting the other IRQs
|
uint irq = TIMER_ALARM_IRQ_NUM(PICO_DEFAULT_TIMER_INSTANCE(), PERIODIC_ALARM_NUM);
|
||||||
irq_set_enabled(TIMER_IRQ_0 + PERIODIC_ALARM_NUM, true); // no harm turning it on
|
irq_set_priority(irq, 0xc0); // don't want timer pre-empting the other IRQs
|
||||||
|
irq_set_enabled(irq, true); // no harm turning it on
|
||||||
}
|
}
|
||||||
|
|
||||||
static void clear_state() {
|
static void clear_state() {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
struct statsomizer {
|
struct statsomizer {
|
||||||
const std::string name;
|
const std::string name;
|
||||||
|
|
|
||||||
|
|
@ -368,7 +368,7 @@ Z_MallocNoUser
|
||||||
|
|
||||||
// found a block big enough
|
// found a block big enough
|
||||||
extra = memblock_size(base) - size;
|
extra = memblock_size(base) - size;
|
||||||
|
|
||||||
if (extra > MINFRAGMENT)
|
if (extra > MINFRAGMENT)
|
||||||
{
|
{
|
||||||
// there will be a free fragment after the allocated block
|
// there will be a free fragment after the allocated block
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue