Snippets

ScorpionIlluminati Sega CD Mode 1

Created by ScorpionIlluminati
/*
 * SEGA CD Mode 1 Player
 * by Chilly Willy
 */

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "hw_md.h"

#define WHITE_TEXT 0x0000
#define GREEN_TEXT 0x2000
#define RED_TEXT   0x4000

extern uint32_t vblank_vector;
extern uint32_t gen_lvl2;
extern uint32_t Sub_Start;
extern uint32_t Sub_End;

extern void Kos_Decomp(uint8_t *src, uint8_t *dst);

static char wait_cmd_ack(void)
{
    char ack = 0;

    while (!ack)
        ack = read_byte(0xA1200F); // wait for acknowledge byte in sub comm port

    return ack;
}

static void wait_do_cmd(char cmd)
{
	while (read_byte(0xA1200F)) ; // wait until Sub-CPU is ready to receive command
    write_byte(0xA1200E, cmd); // set main comm port to command
}

int main(void)
{
    uint16_t buttons = 0, previous = 0, first_track = 0, last_track = 0, curr_track = 0;
    uint8_t *bios;
    char text[44];

    clear_screen();

    /*
     * Check for CD BIOS
     * When a cart is inserted in the MD, the CD hardware is mapped to
     * 0x400000 instead of 0x000000. So the BIOS ROM is at 0x400000, the
     * Program RAM bank is at 0x420000, and the Word RAM is at 0x600000.
     */
    bios = (uint8_t *)0x415800;
    if (memcmp(bios + 0x6D, "SEGA", 4))
    {
        bios = (uint8_t *)0x416000;
        if (memcmp(bios + 0x6D, "SEGA", 4))
        {
            put_str("No CD detected!", RED_TEXT, 20-7, 12);
            while (1) ;
        }
    }
    sprintf(text, "CD Sub-CPU BIOS detected at 0x%6X", (uint32_t)bios);
    put_str(text, GREEN_TEXT, 2, 2);

    /*
     * Reset the Sub-CPU, request the bus
     */
    write_byte(0xA12001, 0x02);
    while (!(read_byte(0xA12001) & 2)) write_byte(0xA12001, 0x02); // wait on bus acknowledge

    /*
     * Decompress Sub-CPU BIOS to Program RAM at 0x00000
     */
    put_str("Decompressing Sub-CPU BIOS", GREEN_TEXT, 2, 3);
    write_word(0xA12002, 0x0002); // no write-protection, bank 0, 2M mode, Word RAM assigned to Sub-CPU
    Kos_Decomp(bios, (uint8_t *)0x420000);

    /*
     * Copy Sub-CPU program to Program RAM at 0x06000
     */
    put_str("Copying Sub-CPU Program", GREEN_TEXT, 2, 4);
    memcpy((void *)0x426000, &Sub_Start, (int)&Sub_End - (int)&Sub_Start);
    if (memcmp((void *)0x426000, &Sub_Start, (int)&Sub_End - (int)&Sub_Start))
    {
        put_str("Failed writing Program RAM!", RED_TEXT, 20-13, 12);
        while (1) ;
    }

    write_byte(0xA1200E, 0x00); // clear main comm port
    write_byte(0xA12002, 0x2A); // write-protect up to 0x05400
    write_byte(0xA12001, 0x01); // clear bus request, deassert reset - allow CD Sub-CPU to run
    while (!(read_byte(0xA12001) & 1)) write_byte(0xA12001, 0x01); // wait on Sub-CPU running
    put_str("Sub-CPU started", GREEN_TEXT, 2, 5);

    /*
     * Set the vertical blank handler to generate Sub-CPU level 2 ints.
     * The Sub-CPU BIOS needs these in order to run.
     */
    write_long((uint32_t)&vblank_vector, (uint32_t)&gen_lvl2);
    set_sr(0x2000); // enable interrupts

    /*
     * Wait for Sub-CPU program to set sub comm port indicating it is running
     */
    while (read_byte(0xA1200F) != 'I')
    {
        static int timeout = 0;
        timeout++;
        if (timeout > 20000)
        {
            put_str("CD failed to start!", RED_TEXT, 20-9, 12);
            while (1) ;
        }
    }

    /*
     * Wait for Sub-CPU to indicate it is ready to receive commands
     */
    while (read_byte(0xA1200F) != 0x00) ;
    put_str("CD initialized and ready to go!", WHITE_TEXT, 20-15, 12);
    delay(300);
    clear_screen();

    put_str("Mode 1 CD Player", WHITE_TEXT, 20-8, 2);
    put_str("DPAD = Change Track START = Check Disc", WHITE_TEXT, 2, 23);
    put_str("A = Play On   B = Play Once   C = Stop", WHITE_TEXT, 2, 24);
    put_str("X = Trk Info Y = Play Repeat Z = Pause", WHITE_TEXT, 2, 25);

    while (1)
    {
        char ack;

        /*
         * Get Disc Info
         */
        wait_do_cmd('D'); // GetDiscInfo command
        ack = wait_cmd_ack();
        if (ack == 'D')
        {
            uint16_t bios_stat;
            uint8_t first_tno, last_tno, drv_vers, flag;
            // command done successfully
            bios_stat = read_word(0xA12020);
            first_tno = read_byte(0xA12022);
            last_tno = read_byte(0xA12023);
            drv_vers = read_byte(0xA12024);
            flag = read_byte(0xA12025);

            first_track = ((int)first_tno & 15) + (int)(first_tno >> 4) * 10;
            last_track = ((int)last_tno & 15) + (int)(last_tno >> 4) * 10;
            if (curr_track < first_track)
                curr_track = first_track;
            if (curr_track > last_track)
                curr_track = last_track;

            put_str("Status:", GREEN_TEXT, 2, 4);
            switch (bios_stat >> 8)
            {
                case 0:
                    put_str("STOPPED             ", WHITE_TEXT, 10, 4);
                    break;
                case 1:
                    put_str("PLAYING             ", WHITE_TEXT, 10, 4);
                    break;
                case 3:
                    put_str("SCANNING            ", WHITE_TEXT, 10, 4);
                    break;
                case 5:
                    put_str("PAUSED              ", WHITE_TEXT, 10, 4);
                    break;
                case 8:
                    put_str("SEEKING             ", WHITE_TEXT, 10, 4);
                    break;
                case 16:
                    put_str("NO DISC             ", WHITE_TEXT, 10, 4);
                    break;
                case 32:
                    put_str("READING TOC         ", WHITE_TEXT, 10, 4);
                    break;
                case 64:
                    put_str("OPEN                ", WHITE_TEXT, 10, 4);
                    break;
                default:
                    if (bios_stat & 0x8000)
                        put_str("NOT READY           ", WHITE_TEXT, 10, 4);
                    else
                        put_str("UNDEFINED           ", WHITE_TEXT, 10, 4);
                    break;
            }
            sprintf(text, "%d", first_track);
            put_str("First Track:    ", GREEN_TEXT, 2, 6);
            put_str(text, WHITE_TEXT, 15, 6);
            sprintf(text, "%d", last_track);
            put_str("Last Track:    ", GREEN_TEXT, 2, 7);
            put_str(text, WHITE_TEXT, 14, 7);
            sprintf(text, "%d", curr_track);
            put_str("Current Track:    ", GREEN_TEXT, 2, 8);
            put_str(text, WHITE_TEXT, 17, 8);

            sprintf(text, "%s %s", (flag & 4) ? "DATA" : "CDDA", (flag & 2) ? "EMP" : "LIN");
            put_str(text, WHITE_TEXT, 2, 10);
            sprintf(text, "%d", drv_vers);
            put_str("Drive Version:    ", GREEN_TEXT, 2, 11);
            put_str(text, WHITE_TEXT, 17, 11);
        }
		write_byte(0xA1200E, 0x00); // acknowledge receipt of command result

        delay(2);
        buttons = get_pad(0) & SEGA_CTRL_BUTTONS;
        if (!(buttons ^ previous))
            continue;

        if (buttons & SEGA_CTRL_START)
        {
            /*
             * Check Disc
             */
            wait_do_cmd('C'); // CheckDisc command
            ack = wait_cmd_ack();
			write_byte(0xA1200E, 0x00); // acknowledge receipt of command result
        }
        if (buttons & SEGA_CTRL_A)
        {
            /*
             * Play from Track to End
             */
            write_word(0xA12010, curr_track);
            write_byte(0xA12012, 0xFF); // play from track to end
            wait_do_cmd('P'); // PlayTrack command
            ack = wait_cmd_ack();
			write_byte(0xA1200E, 0x00); // acknowledge receipt of command result
        }
        if (buttons & SEGA_CTRL_B)
        {
            /*
             * Play Track Once
             */
            write_word(0xA12010, curr_track);
            write_byte(0xA12012, 0x00); // play track once
            wait_do_cmd('P'); // PlayTrack command
            ack = wait_cmd_ack();
			write_byte(0xA1200E, 0x00); // acknowledge receipt of command result
        }
        if (buttons & SEGA_CTRL_C)
        {
            /*
             * Stop Playback
             */
            wait_do_cmd('S'); // StopPlaying command
            ack = wait_cmd_ack();
			write_byte(0xA1200E, 0x00); // acknowledge receipt of command result
        }

        if (buttons & SEGA_CTRL_X)
        {
			uint32_t time_tno;
			uint8_t type;

            /*
             * Get Track Info
             */
            write_word(0xA12010, curr_track);
            wait_do_cmd('T'); // GetTrackInfo command
            ack = wait_cmd_ack();
            time_tno = read_long(0xA12020); // track time: MMSSFFTN
            type = read_byte(0xA12024); // track type: 00 = CDDA, FF = DATA
			write_byte(0xA1200E, 0x00); // acknowledge receipt of command result

			sprintf(text, "%08X", time_tno);
			put_str("Track Time:         ", GREEN_TEXT, 2, 13);
			put_str(text, WHITE_TEXT, 14, 13);
			sprintf(text, "%s", type ? "DATA" : "CDDA");
			put_str("Track Type:     ", GREEN_TEXT, 2, 14);
			put_str(text, WHITE_TEXT, 14, 14);
        }
        if (buttons & SEGA_CTRL_Y)
        {
            /*
             * Play Track and Repeat
             */
            write_word(0xA12010, curr_track);
            write_byte(0xA12012, 0x01); // play track and repeat
            wait_do_cmd('P'); // PlayTrack command
            ack = wait_cmd_ack();
			write_byte(0xA1200E, 0x00); // acknowledge receipt of command result
        }
        if (buttons & SEGA_CTRL_Z)
        {
            /*
             * Pause/Resume
             */
            wait_do_cmd('Z'); // PauseResume command
            ack = wait_cmd_ack();
			write_byte(0xA1200E, 0x00); // acknowledge receipt of command result
        }

        if (buttons & (SEGA_CTRL_DOWN|SEGA_CTRL_RIGHT))
        {
            curr_track++;
            if (curr_track > last_track)
                curr_track = first_track;
        }
        if (buttons & (SEGA_CTRL_UP|SEGA_CTRL_LEFT))
        {
            curr_track--;
            if (curr_track < first_track)
                curr_track = last_track;
        }

        previous = buttons;
    }

    /*
     * Should never reach here due to while condition
     */
    clear_screen ();
    return 0;
}

Comments (0)

HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.