llvm-mos / llvm-mos-sdk

SDK for developing with the llvm-mos compiler

Home Page:https://www.llvm-mos.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

PCE: pce_joypad_read() only returning low nibble?

bcampbell opened this issue · comments

I wrote a little PCE program to display the joypad value, and only the lower bits seem to work. Start, Select and the two fire buttons seem fine, but I get no directions.

It could be my emulator config... but when I run a game rom with the same commandline options the joypad seems to work fine.

Of course, github doesn't let me attach a C file... so here's my test program inlined!

// Show joypad input as hex in top left of screen (and a timer in the top right).
//
// compile:
// $ mos-pce-clang -o demo demo.c
//
// run:
// $ retroarch --verbose --libretro /usr/lib/x86_64-linux-gnu/libretro/mednafen_pce_fast_libretro.so demo

#define PCE_CONFIG_IMPLEMENTATION
#include <pce.h>
// Aiming for largest possible contiguous ROM without any bankswitching.
PCE_ROM_FIXED_BANK_SIZE(6);

// our vram memory map
#define VRAM_BAT 0x0000     // Background Attribute Table (tilemap)
#define VRAM_CG 0x8000      // Character Generator (tile definitions)

// index of first tile
#define BASECHAR (VRAM_CG/32)

// size of map, in tile coords
#define CW 32
#define CH 32

void loadtiles();
void clr(uint8_t c);
void plonkchr(uint8_t cx, uint8_t cy, uint8_t chr);
void plonkhex(uint8_t cx, uint8_t cy, uint8_t v);
void showpad();

volatile uint8_t tick = 0;

__attribute__((interrupt)) void irq_vdc(void) {
  uint8_t status = *IO_VDC_STATUS;
  if (status & VDC_FLAG_VBLANK) {
    ++tick;
  }
}

void waitvbl()
{
    uint8_t tmp = tick;
    while (tmp == tick) {}
}


int main(void) {
    // Set up vblank interrupt
    pce_vdc_irq_vblank_enable();
    pce_irq_enable(IRQ_VDC);
    pce_cpu_irq_enable();
    // set up screen and bg
    pce_vdc_set_resolution(256,240, 0);
    pce_vdc_bg_set_size(VDC_BG_SIZE_32_32);
    pce_vdc_bg_enable();

    loadtiles();

    // set up first bg palette.
    *IO_VCE_COLOR_INDEX = 0x000;
    *IO_VCE_COLOR_DATA = 0x0000;
    for( uint16_t i=1; i<16; ++i) {
        *IO_VCE_COLOR_INDEX = 0x0 + i;
        *IO_VCE_COLOR_DATA = 0xffff;
    }

    clr(0);
    while (1) {
        // print joypad at top left
        uint8_t j = pce_joypad_read();
        plonkhex(0, 0, j);

        // print ticks at top right, just so we can see something happening.
        plonkhex(CW-2, 0, tick);
        waitvbl();

    }
}

// print v as a hex value, at tilemap position cx,cy.
void plonkhex(uint8_t cx, uint8_t cy, uint8_t v)
{
    char buf[2];
    buf[0] = (v >> 4)+1;
    buf[1] = (v & 0x0f)+1;

    plonkchr(cx,cy,buf[0]);
    plonkchr(cx+1,cy,buf[1]);
}

// Some tiledata for showing hex numbers.
// (Just a single bitplane - loadtiles() will duplicate them)
const uint8_t tiles[17*8] = {
    // space
    0b00000000,
    0b00000000,
    0b00000000,
    0b00000000,
    0b00000000,
    0b00000000,
    0b00000000,
    0b00000000,
    // 0
    0b01111100,
    0b11000110,
    0b11000110,
    0b11000110,
    0b11000110,
    0b11000110,
    0b01111100,
    0b00000000,
    // 1
    0b00110000,
    0b00110000,
    0b00110000,
    0b00110000,
    0b00110000,
    0b00110000,
    0b00110000,
    0b00000000,
    // 2
    0b01111100,
    0b11000110,
    0b00000110,
    0b00011100,
    0b00110000,
    0b01100000,
    0b11111110,
    0b00000000,
    // 3
    0b01111100,
    0b11000110,
    0b00000110,
    0b00111100,
    0b00000110,
    0b11000110,
    0b01111100,
    0b00000000,
    // 4
    0b11000000,
    0b11000000,
    0b11000000,
    0b11011000,
    0b11011000,
    0b11111110,
    0b00011000,
    0b00000000,
    // 5
    0b11111110,
    0b11000000,
    0b11000000,
    0b11111100,
    0b00000110,
    0b11000110,
    0b01111100,
    0b00000000,
    // 6
    0b11000000,
    0b11000000,
    0b11000000,
    0b11111100,
    0b11000110,
    0b11000110,
    0b01111100,
    0b00000000,
    // 7
    0b11111110,
    0b00000110,
    0b00000110,
    0b00000110,
    0b00000110,
    0b00000110,
    0b00000110,
    0b00000000,
    // 8
    0b01111100,
    0b11000110,
    0b11000110,
    0b01111100,
    0b11000110,
    0b11000110,
    0b01111100,
    0b00000000,
    // 9
    0b01111100,
    0b11000110,
    0b11000110,
    0b01111110,
    0b00000110,
    0b00000110,
    0b00000110,
    0b00000000,
    // A
    0b01111100,
    0b11000110,
    0b11000110,
    0b11111110,
    0b11000110,
    0b11000110,
    0b11000110,
    0b00000000,
    // B
    0b11111100,
    0b11000110,
    0b11000110,
    0b11111100,
    0b11000110,
    0b11000110,
    0b11111100,
    0b00000000,
    // C
    0b01111100,
    0b11000110,
    0b11000000,
    0b11000000,
    0b11000000,
    0b11000110,
    0b01111100,
    0b00000000,
    // D
    0b11111100,
    0b11000110,
    0b11000110,
    0b11000110,
    0b11000110,
    0b11000110,
    0b11111100,
    0b00000000,
    // E
    0b11111110,
    0b11000000,
    0b11000000,
    0b11111110,
    0b11000000,
    0b11000000,
    0b11111110,
    0b00000000,
    // F
    0b11111110,
    0b11000000,
    0b11000000,
    0b11111000,
    0b11000000,
    0b11000000,
    0b11000000,
    0b00000000,
};


// load our tiles into VRAM (starting at VRAM_CG)
void loadtiles()
{
    const uint8_t *src = tiles;
    pce_vdc_set_copy_word();
    for (uint8_t t=0; t<17; ++t) {
        uint8_t buf[4*8];
        for (uint8_t y=0; y<8; ++y) {
            uint8_t b = *src++;
            // use the src data for all 4 bitplanes
            buf[y*2 + 0] = b;
            buf[y*2 + 1] = b;
            buf[16 + y*2 + 0] = b;
            buf[16 + y*2 + 1] = b;
        }
        pce_vdc_copy_to_vram((VRAM_CG+(t*32))/2, (const void *)buf, sizeof(buf));
    }
}

// set a single tile at cx,cy
void plonkchr(uint8_t cx, uint8_t cy, uint8_t chr)
{
    uint16_t dest = VRAM_BAT + ((cy*CW + cx)*2);

    uint16_t tmp = BASECHAR + chr;
    pce_vdc_copy_to_vram(dest/2, &tmp, 2);
}


// clear the screen using given tile
void clr(uint8_t chr)
{
    uint16_t dest = VRAM_BAT;

    for (uint8_t cy = 0; cy < CH; ++cy)
    {
        uint16_t linebuf[CW];
        for (uint8_t cx = 0; cx < CW; ++cx)
        {
            linebuf[cx] = BASECHAR + chr;
        }
        pce_vdc_copy_to_vram(dest/2, linebuf, sizeof(linebuf));
        dest += (CW*2);
    }
}



commented

Did you find the implementation of pce_joypad_read()?

uint8_t pce_joypad_wr(uint8_t value) {
uint8_t result;
*IO_JOYPAD = value;
__attribute__((leaf)) asm volatile("sxy\nsxy\nsxy\nsxy\n"); // burn cycles
return *IO_JOYPAD;
}
__attribute__((noinline)) uint8_t pce_joypad_next(void) {
uint8_t low = pce_joypad_wr(0);
uint8_t high = pce_joypad_wr(JOYPAD_SEL);
return (low & 0xF) | (high << 4);
}
uint8_t pce_joypad_read(void) {
pce_joypad_wr(JOYPAD_SEL);
pce_joypad_wr(JOYPAD_SEL | JOYPAD_CLR);
return pce_joypad_next();
}

JOYPAD_SEL is

and IO_JOYPAD

#define IO_JOYPAD ((volatile uint8_t *)0x1000)

I am not familiar with PC Engine to be intimate with that code, though briefly correlating against example code at https://www.chibiakumas.com/6502/platform2.php#LessonP14 , everything does check out. You could try copy-pasting the implementation of those functions to your own code and playing around with the constructs to see if you can find an issue.

The only thing that I find different is that the above code sleep 4x SXY instructions for 4x3 = 12 clock cycles, whereas the code I found on the above web page waits for a PHA+PLA+NOP+NOP = 3+4+2+2 = 11 clock cycles. Hrm, so that wouldn't explain a problem either.

commented

There is a mention however

Before we can start reading, we need to initialize the 'Multitap' (the hardware that toggles the joypads)...
to do this we just write a #1 then #3  to port $1000... we need a short delay after each write.... 

Maybe that is what might be missing?

The multitap handling is broken in some manner (and 6-button gamepad handling is not implemented at all).

You can easily verify this in Mednafen by setting pce.input.multitap to 0, or in Ares by using the GUI.