Usercalls and _LEVEL3

Ask anything your want about Mega/SegaCD programming.

Moderator: Mask of Destiny

Post Reply
HCKTROX
Interested
Posts: 27
Joined: Wed Mar 24, 2010 1:15 am
Location: Chile

Usercalls and _LEVEL3

Post by HCKTROX » Sun Sep 08, 2013 3:05 am

Hello everyone.

I have been trying to implement a disassembled PCM code in my game.

The dissassemble seems to be fine (I already tested a recompiled version of the same file in the game where I took it), but, the interrupts doesn't seem to work fine on mine game.

According to certain parts of the manual and the way it's done in the game I took the code from, it uses the interrupt Level3 (which has timing) to run the code. And the code is done like this:

http://pastebin.com/KpKQUu8A

However, it only makes the Sub-CPU to be stuck at $5EE (part of BIOS/CD-SYSTEM section), doing a btst #0,($5EA4).w (_Usermode-2). With Gens's debugging tool, I see that magically a random $01 number appears there, so that's what what makes it stick there.

Any ideas of what is going on?
Thanks
skype: hcktrox

Chilly Willy
Very interested
Posts: 2984
Joined: Fri Aug 17, 2007 9:33 pm

Post by Chilly Willy » Sun Sep 08, 2013 7:25 am

For a level 3 int to fire, you have to set the int mask to $2200, not $2300. That's masking the int.

Here's how I set the int and use it in my libpcm...

Code: Select all

    /* General use timer */
    .equ    TIMER,    0x8030
    .equ    INT_MASK, 0x8032
    .equ    _LEVEL3,  0x5F82        /* TIMER INTERRUPT jump vector */

...

| void pcm_set_timer(uint16_t bpm);
| TIMER = 32552 / (bpm * 2) - 1
|   should be 32552 * 5 / (bpm * 2) - 1, but that's too big for TIMER, so
|   we leave out the * 5 and compensate in the int handler
    .global pcm_set_timer
pcm_set_timer:
    move.l  #32552,d0
    move.l  4(sp),d1
    cmpi.l  #64,d1
    bhs.b   0f
    moveq   #64,d1                  /* lowest tempo TIMER can handle */
0:
    add.w   d1,d1
    divu.w  d1,d0
    subq.w  #1,d0
    ble.b   1f                      /* safety check */
    move.w  d0,TIMER.w
1:
    rts

| void pcm_stop_timer(void);
    .global pcm_stop_timer
pcm_stop_timer:
    move    #0x2700,sr              /* disable interrupts */

    move.w  #0,TIMER.w              /* stop General Timer */
    move.w  INT_MASK.w,d0
    andi.w  #0xFFF7,d0
    move.w  d0,INT_MASK.w           /* disable General Timer interrupt */

    move    #0x2000,sr              /* enable interrupts */
    rts

| void pcm_start_timer(void (*callback)(void));
    .global pcm_start_timer
pcm_start_timer:
    move    #0x2700,sr              /* disable interrupts */

    move.l  4(sp),int3_callback     /* set callback vector */
    move.w  #0,int3_cntr            /* clear int counter */

    move.w  #0x4EF9,_LEVEL3.w
    move.l  #timer_int,_LEVEL3+2.w  /* set level 3 int vector for timer */

    move.w  #129,TIMER.w            /* 125 BPM */
    move.w  INT_MASK.w,d0
    ori.w   #0x0008,d0
    move.w  d0,INT_MASK.w           /* enable General Timer interrupt */

    move    #0x2000,sr              /* enable interrupts */
    rts

timer_int:
    move.l  d0,-(sp)
    move.w  int3_cntr,d0
    addq.w  #1,d0
    cmpi.w  #5,d0
    blo.b   0f                      /* once every 5 ints for actual beats per minute rate */

    movem.l d1/a0-a1,-(sp)
    movea.l int3_callback,a1
    jsr     (a1)
    movem.l (sp)+,d1/a0-a1
    moveq   #0,d0
0:
    move.w  d0,int3_cntr
    move.l  (sp)+,d0
    rte

Note that I don't bother saving or restoring the int since the system doesn't use it, and my program is the only thing running... the SegaCD doesn't multi-task, after all. :lol:

Note that int3_callback is a pointer to a plain C function, like this:

Code: Select all

    pcm_start_timer(&callback);

...

static void callback(void)
{
...
}

Post Reply