mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2024-11-26 15:45:41 +00:00
Initial import of new Supermodel code.
This commit is contained in:
parent
1848a427ec
commit
77cb1b8a76
258
BlockFile.cpp
Normal file
258
BlockFile.cpp
Normal file
|
@ -0,0 +1,258 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* BlockFile.cpp
|
||||||
|
*
|
||||||
|
* Block format container file management. Implementation of the CBlockFile
|
||||||
|
* class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "Supermodel.h"
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Output Functions
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void CBlockFile::ReadString(char *str, unsigned strLen, unsigned maxLen)
|
||||||
|
{
|
||||||
|
if (NULL == fp)
|
||||||
|
return;
|
||||||
|
if (strLen>maxLen)
|
||||||
|
strLen = maxLen;
|
||||||
|
fread(str, sizeof(char), strLen, fp);
|
||||||
|
str[strLen] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned CBlockFile::ReadBytes(void *data, unsigned numBytes)
|
||||||
|
{
|
||||||
|
if (NULL == fp)
|
||||||
|
return 0;
|
||||||
|
return fread(data, sizeof(UINT8), numBytes, fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned CBlockFile::ReadDWord(UINT32 *data)
|
||||||
|
{
|
||||||
|
if (NULL == fp)
|
||||||
|
return 0;
|
||||||
|
fread(data, sizeof(UINT32), 1, fp);
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBlockFile::UpdateBlockSize(void)
|
||||||
|
{
|
||||||
|
long int curPos;
|
||||||
|
unsigned newBlockSize;
|
||||||
|
|
||||||
|
if (NULL == fp)
|
||||||
|
return;
|
||||||
|
curPos = ftell(fp); // save current file position
|
||||||
|
fseek(fp, blockStartPos, SEEK_SET);
|
||||||
|
newBlockSize = curPos - blockStartPos;
|
||||||
|
fwrite(&newBlockSize, sizeof(UINT32), 1, fp);
|
||||||
|
fseek(fp, curPos, SEEK_SET); // go back
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBlockFile::WriteByte(UINT8 data)
|
||||||
|
{
|
||||||
|
if (NULL == fp)
|
||||||
|
return;
|
||||||
|
fwrite(&data, sizeof(UINT8), 1, fp);
|
||||||
|
UpdateBlockSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBlockFile::WriteDWord(UINT32 data)
|
||||||
|
{
|
||||||
|
if (NULL == fp)
|
||||||
|
return;
|
||||||
|
fwrite(&data, sizeof(UINT32), 1, fp);
|
||||||
|
UpdateBlockSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBlockFile::WriteBytes(const void *data, unsigned numBytes)
|
||||||
|
{
|
||||||
|
if (NULL == fp)
|
||||||
|
return;
|
||||||
|
fwrite(data, sizeof(UINT8), numBytes, fp);
|
||||||
|
UpdateBlockSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBlockFile::WriteBlockHeader(const char *name, const char *comment)
|
||||||
|
{
|
||||||
|
unsigned nameLen, commentLen;
|
||||||
|
const char nullComment[1] = {'\0'};
|
||||||
|
|
||||||
|
if (NULL == fp)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (comment == NULL)
|
||||||
|
comment = nullComment;
|
||||||
|
|
||||||
|
nameLen = strlen(name);
|
||||||
|
commentLen = strlen(comment);
|
||||||
|
if (nameLen > 1024)
|
||||||
|
nameLen = 1024;
|
||||||
|
if (commentLen > 1024)
|
||||||
|
commentLen = 1024;
|
||||||
|
|
||||||
|
// Record current block starting position
|
||||||
|
blockStartPos = ftell(fp);
|
||||||
|
|
||||||
|
// Write the total block length field
|
||||||
|
WriteDWord(0); // will be automatically updated as we write the file
|
||||||
|
|
||||||
|
// Write name and comment lengths
|
||||||
|
WriteDWord(nameLen+1);
|
||||||
|
WriteDWord(commentLen+1);
|
||||||
|
WriteBytes(name, nameLen);
|
||||||
|
WriteByte(0);
|
||||||
|
WriteBytes(comment, commentLen);
|
||||||
|
WriteByte(0);
|
||||||
|
|
||||||
|
// Record the start of the current data section
|
||||||
|
dataStartPos = ftell(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Block Format Container File Implementation
|
||||||
|
|
||||||
|
Files are just a consecutive array of blocks that must be searched.
|
||||||
|
|
||||||
|
Block Format
|
||||||
|
------------
|
||||||
|
blockLength (UINT32) Total length of block in bytes.
|
||||||
|
nameLength (UINT32) Length of name field including terminating 0 (up to
|
||||||
|
1025).
|
||||||
|
commentLength (UINT32) Same as above, but for comment string.
|
||||||
|
name ... Name string (null-terminated, up to 1025 bytes).
|
||||||
|
comment ... Comment string (same as above).
|
||||||
|
data ... Raw data (blockLength - total header size).
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
unsigned CBlockFile::Read(void *data, unsigned numBytes)
|
||||||
|
{
|
||||||
|
if (mode == 'r')
|
||||||
|
return ReadBytes(data, numBytes);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBlockFile::Write(const void *data, unsigned numBytes)
|
||||||
|
{
|
||||||
|
if (mode == 'w')
|
||||||
|
WriteBytes(data, numBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBlockFile::NewBlock(const char *name, const char *comment)
|
||||||
|
{
|
||||||
|
if (mode == 'w')
|
||||||
|
WriteBlockHeader(name, comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL CBlockFile::FindBlock(const char *name)
|
||||||
|
{
|
||||||
|
long int curPos = 0;
|
||||||
|
unsigned blockLen, nameLen, commentLen;
|
||||||
|
|
||||||
|
if (mode != 'r')
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
fseek(fp, 0, SEEK_SET);
|
||||||
|
|
||||||
|
while (curPos < fileSize)
|
||||||
|
{
|
||||||
|
blockStartPos = curPos;
|
||||||
|
|
||||||
|
// Read header
|
||||||
|
curPos += ReadDWord(&blockLen);
|
||||||
|
curPos += ReadDWord(&nameLen);
|
||||||
|
curPos += ReadDWord(&commentLen);
|
||||||
|
ReadString(strBuf,nameLen,1025);
|
||||||
|
|
||||||
|
// Is this the block we want?
|
||||||
|
if (!strcmp(strBuf,name))
|
||||||
|
{
|
||||||
|
fseek(fp, blockStartPos+12+nameLen+commentLen, SEEK_SET); // move to beginning of data
|
||||||
|
dataStartPos = ftell(fp);
|
||||||
|
return OKAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move to next block
|
||||||
|
fseek(fp, blockStartPos+blockLen, SEEK_SET);
|
||||||
|
curPos = blockStartPos+blockLen;
|
||||||
|
if (blockLen == 0) // this would never advance
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL CBlockFile::Create(const char *file, const char *headerName, const char *comment)
|
||||||
|
{
|
||||||
|
fp = fopen(file, "wb");
|
||||||
|
if (NULL == fp)
|
||||||
|
return FAIL;
|
||||||
|
mode = 'w';
|
||||||
|
WriteBlockHeader(headerName, comment);
|
||||||
|
return OKAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL CBlockFile::Load(const char *file)
|
||||||
|
{
|
||||||
|
fp = fopen(file, "rb");
|
||||||
|
if (NULL == fp)
|
||||||
|
return FAIL;
|
||||||
|
mode = 'r';
|
||||||
|
|
||||||
|
// TODO: is this a valid block file?
|
||||||
|
|
||||||
|
// Get the file size
|
||||||
|
fseek(fp, 0, SEEK_END);
|
||||||
|
fileSize = ftell(fp);
|
||||||
|
fseek(fp, 0, SEEK_SET);
|
||||||
|
|
||||||
|
return OKAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBlockFile::Close(void)
|
||||||
|
{
|
||||||
|
if (fp != NULL)
|
||||||
|
fclose(fp);
|
||||||
|
fp = NULL;
|
||||||
|
mode = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
CBlockFile::CBlockFile(void)
|
||||||
|
{
|
||||||
|
fp = NULL;
|
||||||
|
mode = 0; // neither reading nor writing (do nothing)
|
||||||
|
}
|
||||||
|
|
||||||
|
CBlockFile::~CBlockFile(void)
|
||||||
|
{
|
||||||
|
if (fp != NULL) // in case user forgot
|
||||||
|
fclose(fp);
|
||||||
|
fp = NULL;
|
||||||
|
mode = 0;
|
||||||
|
}
|
170
BlockFile.h
Normal file
170
BlockFile.h
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* BlockFile.h
|
||||||
|
*
|
||||||
|
* Header file for block format container file management.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDED_BLOCKFILE_H
|
||||||
|
#define INCLUDED_BLOCKFILE_H
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CBlockFile:
|
||||||
|
*
|
||||||
|
* Block format container file. The file format is a series of consecutive,
|
||||||
|
* variable-length blocks referenced by unique name strings.
|
||||||
|
*
|
||||||
|
* All strings (comments and names) will be truncated to 1024 bytes, not
|
||||||
|
* including the null terminator.
|
||||||
|
*
|
||||||
|
* Members do not generate any output messages.
|
||||||
|
*/
|
||||||
|
class CBlockFile
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
* Read(data, numBytes):
|
||||||
|
*
|
||||||
|
* Reads data from the current file position.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* data Buffer to read to.
|
||||||
|
* numBytes Number of bytes to read.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Number of bytes read. If not the same as numBytes, an error
|
||||||
|
* occurred.
|
||||||
|
*/
|
||||||
|
unsigned Read(void *data, unsigned numBytes);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FindBlock(name):
|
||||||
|
*
|
||||||
|
* Scans the file for a block with the given name string. When it is found,
|
||||||
|
* the file pointer is set to the beginning of the data region.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* name Name of block to locate.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* OKAY if found, FAIL if unable to locate.
|
||||||
|
*/
|
||||||
|
BOOL FindBlock(const char *name);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write(data, numBytes):
|
||||||
|
*
|
||||||
|
* Outputs data at the current file pointer position. Updates the block
|
||||||
|
* header appropriately.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* data Data to write.
|
||||||
|
* numBytes Number of bytes to write.
|
||||||
|
*/
|
||||||
|
void Write(const void *data, unsigned numBytes);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NewBlock(name, comment):
|
||||||
|
*
|
||||||
|
* Begins a new block. Writes the block header and sets the file pointer to
|
||||||
|
* the beginning of its data area.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* name Block name. Must be unique and not NULL.
|
||||||
|
* comment Comment string to embed in the block header.
|
||||||
|
*/
|
||||||
|
void NewBlock(const char *title, const char *comment);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create(file, headerName, comment):
|
||||||
|
*
|
||||||
|
* Opens a block file for writing and creates the header block. This
|
||||||
|
* function must be called before attempting to write data. Otherwise, all
|
||||||
|
* write commands will be silently ignored. Read commands will be ignored
|
||||||
|
* and will always return 0's.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* file File path.
|
||||||
|
* headerName Block name for header. Must be unique and not NULL.
|
||||||
|
* comment Comment string that will be embedded into file header.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* OKAY if successfully opened, otherwise FAIL.
|
||||||
|
*/
|
||||||
|
BOOL Create(const char *file, const char *headerName, const char *comment);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Load(file):
|
||||||
|
*
|
||||||
|
* Open a block file file for reading.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* file File path.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* OKAY if successfully opened and confirmed to be a valid Supermodel
|
||||||
|
* block file, otherwise FAIL. If the file could not be opened, all
|
||||||
|
* subsequent operations will be silently ignored (reads will return
|
||||||
|
* 0's). Write commands will be ignored.
|
||||||
|
*/
|
||||||
|
BOOL Load(const char *file);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Close(void):
|
||||||
|
*
|
||||||
|
* Closes the file.
|
||||||
|
*/
|
||||||
|
void Close(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CBlockFile(void):
|
||||||
|
* ~CBlockFile(void):
|
||||||
|
*
|
||||||
|
* Constructor and destructor.
|
||||||
|
*/
|
||||||
|
CBlockFile(void);
|
||||||
|
~CBlockFile(void);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Helper functions
|
||||||
|
void ReadString(char *str, unsigned strLen, unsigned maxLen);
|
||||||
|
unsigned ReadBytes(void *data, unsigned numBytes);
|
||||||
|
unsigned ReadDWord(UINT32 *data);
|
||||||
|
void UpdateBlockSize(void);
|
||||||
|
void WriteByte(UINT8 data);
|
||||||
|
void WriteDWord(UINT32 data);
|
||||||
|
void WriteBytes(const void *data, unsigned numBytes);
|
||||||
|
void WriteBlockHeader(const char *name, const char *comment);
|
||||||
|
|
||||||
|
// File state data
|
||||||
|
char strBuf[1026]; // buffers up to a 1024-character string, its terminator, and an extra terminator (just in case)
|
||||||
|
FILE *fp;
|
||||||
|
int mode; // 'r' for read, 'w' for write
|
||||||
|
long int fileSize; // size of file in bytes
|
||||||
|
long int blockStartPos; // points to beginning of current block (or file) header
|
||||||
|
long int dataStartPos; // points to beginning of current block's data section
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // INCLUDED_BLOCKFILE_H
|
8938
CPU/68K/Make68K.c
Normal file
8938
CPU/68K/Make68K.c
Normal file
File diff suppressed because it is too large
Load diff
1299
CPU/68K/README.TXT
Normal file
1299
CPU/68K/README.TXT
Normal file
File diff suppressed because it is too large
Load diff
158
CPU/68K/Turbo68K.h
Normal file
158
CPU/68K/Turbo68K.h
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
/*
|
||||||
|
* Turbo68K: Motorola 680X0 emulator
|
||||||
|
* Copyright 2000-2002 Bart Trzynadlowski, see "README.TXT" for terms of use
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _TURBO68K_H_
|
||||||
|
#define _TURBO68K_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* Generic Macros */
|
||||||
|
|
||||||
|
#define TURBO68K_OKAY 0 /* no error */
|
||||||
|
#define TURBO68K_NULL 0 /* null */
|
||||||
|
#define TURBO68K_SUPERVISOR 0 /* affects supervisor space */
|
||||||
|
#define TURBO68K_USER 1 /* function affects user space */
|
||||||
|
#define TURBO68K_UNINITIALIZED 15 /* uninitialized interrupt */
|
||||||
|
#define TURBO68K_SPURIOUS 24 /* spurious interrupt */
|
||||||
|
#define TURBO68K_AUTOVECTOR 256 /* autovectored interrupt */
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* Error Codes */
|
||||||
|
|
||||||
|
#define TURBO68K_ERROR_FETCH 1 /* fetch error (PC bounds) */
|
||||||
|
#define TURBO68K_ERROR_INVINST 2 /* invalid instruction */
|
||||||
|
#define TURBO68K_ERROR_INTLEVEL 3 /* invalid interrupt level */
|
||||||
|
#define TURBO68K_ERROR_INTVECTOR 4 /* invalid interrupt vector */
|
||||||
|
#define TURBO68K_ERROR_INTPENDING 5 /* level already pending */
|
||||||
|
#define TURBO68K_ERROR_STACKFRAME 6 /* unhandled stack frame */
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* Data Types */
|
||||||
|
|
||||||
|
typedef unsigned int TURBO68K_UINT32; /* unsigned 32-bit */
|
||||||
|
typedef signed int TURBO68K_INT32; /* signed 32-bit */
|
||||||
|
typedef unsigned short TURBO68K_UINT16; /* unsigned 16-bit */
|
||||||
|
typedef signed short TURBO68K_INT16; /* signed 16-bit */
|
||||||
|
typedef unsigned char TURBO68K_UINT8; /* unsigned 8-bit */
|
||||||
|
typedef signed char TURBO68K_INT8; /* signed 8-bit */
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* Data Structures */
|
||||||
|
|
||||||
|
struct TURBO68K_FETCHREGION
|
||||||
|
{
|
||||||
|
TURBO68K_UINT32 base;
|
||||||
|
TURBO68K_UINT32 limit;
|
||||||
|
TURBO68K_UINT32 ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TURBO68K_DATAREGION
|
||||||
|
{
|
||||||
|
TURBO68K_UINT32 base;
|
||||||
|
TURBO68K_UINT32 limit;
|
||||||
|
TURBO68K_UINT32 ptr;
|
||||||
|
void *handler;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TURBO68K_CONTEXT_68000
|
||||||
|
{
|
||||||
|
void *fetch, *pcfetch;
|
||||||
|
void *read_byte, *read_word, *read_long;
|
||||||
|
void *write_byte, *write_word, *write_long;
|
||||||
|
void *super_fetch, *super_pcfetch;
|
||||||
|
void *super_read_byte, *super_read_word, *super_read_long;
|
||||||
|
void *super_write_byte, *super_write_word, *super_write_long;
|
||||||
|
void *user_fetch, *user_pcfetch;
|
||||||
|
void *user_read_byte, *user_read_word, *user_read_long;
|
||||||
|
void *user_write_byte, *user_write_word, *user_write_long;
|
||||||
|
TURBO68K_UINT32 intr[8], cycles, remaining;
|
||||||
|
TURBO68K_UINT32 d[8], a[8], sp, sr, pc, status;
|
||||||
|
void *InterruptAcknowledge;
|
||||||
|
void *Reset;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TURBO68K_CONTEXT_68010
|
||||||
|
{
|
||||||
|
void *fetch, *pcfetch;
|
||||||
|
void *read_byte, *read_word, *read_long;
|
||||||
|
void *write_byte, *write_word, *write_long;
|
||||||
|
void *super_fetch, *super_pcfetch;
|
||||||
|
void *super_read_byte, *super_read_word, *super_read_long;
|
||||||
|
void *super_write_byte, *super_write_word, *super_write_long;
|
||||||
|
void *user_fetch, *user_pcfetch;
|
||||||
|
void *user_read_byte, *user_read_word, *user_read_long;
|
||||||
|
void *user_write_byte, *user_write_word, *user_write_long;
|
||||||
|
TURBO68K_UINT32 intr[8], cycles, remaining;
|
||||||
|
TURBO68K_UINT32 d[8], a[8], sp, sr, pc, fc, vbr, status;
|
||||||
|
void *InterruptAcknowledge;
|
||||||
|
void *Reset, *Bkpt;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* Functions and External Data */
|
||||||
|
|
||||||
|
#define TURBO68K_ID(ID) \
|
||||||
|
\
|
||||||
|
extern struct TURBO68K_CONTEXT_68000 ID##turbo68kcontext_68000; \
|
||||||
|
extern struct TURBO68K_CONTEXT_68010 ID##turbo68kcontext_68010; \
|
||||||
|
\
|
||||||
|
TURBO68K_INT32 ID##Turbo68KInit(); \
|
||||||
|
TURBO68K_INT32 ID##Turbo68KReset(); \
|
||||||
|
TURBO68K_INT32 ID##Turbo68KRun(TURBO68K_INT32); \
|
||||||
|
TURBO68K_INT32 ID##Turbo68KProcessInterrupts(); \
|
||||||
|
TURBO68K_INT32 ID##Turbo68KInterrupt(TURBO68K_INT32, TURBO68K_UINT32); \
|
||||||
|
TURBO68K_INT32 ID##Turbo68KCancelInterrupt(TURBO68K_INT32); \
|
||||||
|
\
|
||||||
|
TURBO68K_UINT32 ID##Turbo68KReadPC(); \
|
||||||
|
\
|
||||||
|
void ID##Turbo68KSetFetch(void *, TURBO68K_INT32); \
|
||||||
|
void ID##Turbo68KSetPCFetch(void *, TURBO68K_INT32); \
|
||||||
|
void ID##Turbo68KSetReadByte(void *, TURBO68K_INT32); \
|
||||||
|
void ID##Turbo68KSetReadWord(void *, TURBO68K_INT32); \
|
||||||
|
void ID##Turbo68KSetReadLong(void *, TURBO68K_INT32); \
|
||||||
|
void ID##Turbo68KSetWriteByte(void *, TURBO68K_INT32); \
|
||||||
|
void ID##Turbo68KSetWriteWord(void *, TURBO68K_INT32); \
|
||||||
|
void ID##Turbo68KSetWriteLong(void *, TURBO68K_INT32); \
|
||||||
|
void *ID##Turbo68KGetFetch(TURBO68K_INT32); \
|
||||||
|
void *ID##Turbo68KGetPCFetch(TURBO68K_INT32); \
|
||||||
|
void *ID##Turbo68KGetReadByte(TURBO68K_INT32); \
|
||||||
|
void *ID##Turbo68KGetReadWord(TURBO68K_INT32); \
|
||||||
|
void *ID##Turbo68KGetReadLong(TURBO68K_INT32); \
|
||||||
|
void *ID##Turbo68KGetWriteByte(TURBO68K_INT32); \
|
||||||
|
void *ID##Turbo68KGetWriteWord(TURBO68K_INT32); \
|
||||||
|
void *ID##Turbo68KGetWriteLong(TURBO68K_INT32); \
|
||||||
|
\
|
||||||
|
TURBO68K_UINT8 *ID##Turbo68KFetchPtr(TURBO68K_UINT32); \
|
||||||
|
TURBO68K_UINT8 ID##Turbo68KReadByte(TURBO68K_UINT32); \
|
||||||
|
TURBO68K_UINT16 ID##Turbo68KReadWord(TURBO68K_UINT32); \
|
||||||
|
TURBO68K_UINT32 ID##Turbo68KReadLong(TURBO68K_UINT32); \
|
||||||
|
void ID##Turbo68KWriteByte(TURBO68K_UINT32, TURBO68K_UINT8); \
|
||||||
|
void ID##Turbo68KWriteWord(TURBO68K_UINT32, TURBO68K_UINT16);\
|
||||||
|
void ID##Turbo68KWriteLong(TURBO68K_UINT32, TURBO68K_UINT32);\
|
||||||
|
\
|
||||||
|
void ID##Turbo68KSetContext(void *); \
|
||||||
|
void ID##Turbo68KGetContext(void *); \
|
||||||
|
TURBO68K_UINT32 ID##Turbo68KGetContextSize(); \
|
||||||
|
\
|
||||||
|
void ID##Turbo68KClearCycles(); \
|
||||||
|
void ID##Turbo68KFreeTimeSlice(); \
|
||||||
|
TURBO68K_INT32 ID##Turbo68KGetElapsedCycles(); \
|
||||||
|
|
||||||
|
TURBO68K_ID( ); /* default identifiers begin with nothing */
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _TURBO68K_H_ */
|
1260
CPU/PowerPC/PPCDisasm.cpp
Normal file
1260
CPU/PowerPC/PPCDisasm.cpp
Normal file
File diff suppressed because it is too large
Load diff
58
CPU/PowerPC/PPCDisasm.h
Normal file
58
CPU/PowerPC/PPCDisasm.h
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PPCDisasm.h
|
||||||
|
*
|
||||||
|
* Header file for PowerPC disassembler.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDED_PPCDISASM_H
|
||||||
|
#define INCLUDED_PPCDISASM_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DisassemblePowerPC(op, vpc, mnem, oprs, simplify):
|
||||||
|
*
|
||||||
|
* Disassembles one PowerPC 603e instruction.
|
||||||
|
*
|
||||||
|
* A non-zero return code indicates that the instruction could not be
|
||||||
|
* recognized or that the operands to an instruction were invalid. To
|
||||||
|
* determine which case occured, check if mnem[0] == '\0'. If it does not,
|
||||||
|
* then the latter case happened.
|
||||||
|
*
|
||||||
|
* Arguments:
|
||||||
|
* op Instruction word to disassemble.
|
||||||
|
* vpc Current instruction address.
|
||||||
|
* mnem Buffer to write instruction mnemonic to. If no
|
||||||
|
* instruction was decoded, mnem[0] and oprs[0] will be set
|
||||||
|
* to '\0'.
|
||||||
|
* oprs Buffer to write any operands to.
|
||||||
|
* simplify If non-zero, simplified forms of instructions will be
|
||||||
|
* printed in certain cases.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* OKAY if successful, FAIL if the instruction was unrecognized or had an
|
||||||
|
* invalid form (see note above in function description.)
|
||||||
|
*/
|
||||||
|
extern BOOL DisassemblePowerPC(UINT32 op, UINT32 vpc, char *mnem, char *oprs,
|
||||||
|
BOOL simplify);
|
||||||
|
|
||||||
|
#endif // INCLUDED_PPCDISASM_H
|
1046
CPU/PowerPC/ppc.cpp
Normal file
1046
CPU/PowerPC/ppc.cpp
Normal file
File diff suppressed because it is too large
Load diff
366
CPU/PowerPC/ppc.h
Normal file
366
CPU/PowerPC/ppc.h
Normal file
|
@ -0,0 +1,366 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ppc.h
|
||||||
|
*
|
||||||
|
* Header file for PowerPC emulator.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDED_PPC_H
|
||||||
|
#define INCLUDED_PPC_H
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Definitions
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#define SPR_XER 1 /* Fixed Point Exception Register Read / Write */
|
||||||
|
#define SPR_LR 8 /* Link Register Read / Write */
|
||||||
|
#define SPR_CTR 9 /* Count Register Read / Write */
|
||||||
|
#define SPR_SRR0 26 /* Save/Restore Register 0 Read / Write */
|
||||||
|
#define SPR_SRR1 27 /* Save/Restore Register 1 Read / Write */
|
||||||
|
#define SPR_SPRG0 272 /* SPR General 0 Read / Write */
|
||||||
|
#define SPR_SPRG1 273 /* SPR General 1 Read / Write */
|
||||||
|
#define SPR_SPRG2 274 /* SPR General 2 Read / Write */
|
||||||
|
#define SPR_SPRG3 275 /* SPR General 3 Read / Write */
|
||||||
|
#define SPR_PVR 287 /* Processor Version Number Read Only */
|
||||||
|
|
||||||
|
#define SPR403_ICDBDR 0x3D3 /* 406GA Instruction Cache Debug Data Register Rad Only */
|
||||||
|
#define SPR403_ESR 0x3D4 /* 406GA Exception Syndrome Register Read / Write */
|
||||||
|
#define SPR403_DEAR 0x3D5 /* 406GA Data Exception Address Register Read Only */
|
||||||
|
#define SPR403_EVPR 0x3D6 /* 406GA Exception Vector Prefix Register Read / Write */
|
||||||
|
#define SPR403_CDBCR 0x3D7 /* 406GA Cache Debug Control Register Read/Write */
|
||||||
|
#define SPR403_TSR 0x3D8 /* 406GA Timer Status Register Read / Clear */
|
||||||
|
#define SPR403_TCR 0x3DA /* 406GA Timer Control Register Read / Write */
|
||||||
|
#define SPR403_PIT 0x3DB /* 406GA Programmable Interval Timer Read / Write */
|
||||||
|
#define SPR403_TBHI 988 /* 406GA Time Base High Read / Write */
|
||||||
|
#define SPR403_TBLO 989 /* 406GA Time Base Low Read / Write */
|
||||||
|
#define SPR403_SRR2 0x3DE /* 406GA Save/Restore Register 2 Read / Write */
|
||||||
|
#define SPR403_SRR3 0x3DF /* 406GA Save/Restore Register 3 Read / Write */
|
||||||
|
#define SPR403_DBSR 0x3F0 /* 406GA Debug Status Register Read / Clear */
|
||||||
|
#define SPR403_DBCR 0x3F2 /* 406GA Debug Control Register Read / Write */
|
||||||
|
#define SPR403_IAC1 0x3F4 /* 406GA Instruction Address Compare 1 Read / Write */
|
||||||
|
#define SPR403_IAC2 0x3F5 /* 406GA Instruction Address Compare 2 Read / Write */
|
||||||
|
#define SPR403_DAC1 0x3F6 /* 406GA Data Address Compare 1 Read / Write */
|
||||||
|
#define SPR403_DAC2 0x3F7 /* 406GA Data Address Compare 2 Read / Write */
|
||||||
|
#define SPR403_DCCR 0x3FA /* 406GA Data Cache Cacheability Register Read / Write */
|
||||||
|
#define SPR403_ICCR 0x3FB /* 406GA I Cache Cacheability Registe Read / Write */
|
||||||
|
#define SPR403_PBL1 0x3FC /* 406GA Protection Bound Lower 1 Read / Write */
|
||||||
|
#define SPR403_PBU1 0x3FD /* 406GA Protection Bound Upper 1 Read / Write */
|
||||||
|
#define SPR403_PBL2 0x3FE /* 406GA Protection Bound Lower 2 Read / Write */
|
||||||
|
#define SPR403_PBU2 0x3FF /* 406GA Protection Bound Upper 2 Read / Write */
|
||||||
|
|
||||||
|
#define SPR403_SGR 0x3b9 /* 403GCX Storage Guarded Register */
|
||||||
|
#define SPR403_DCWR 0x3ba /* 403GCX Data Cache Write Through */
|
||||||
|
#define SPR403_PID 0x3b1 /* 403GCX Process ID */
|
||||||
|
#define SPR403_TBHU 0x3cc /* 403GCX Time Base High User-mode */
|
||||||
|
#define SPR403_TBLU 0x3cd /* 403GCX Time Base Low User-mode */
|
||||||
|
|
||||||
|
#define SPR603E_DSISR 18 /* 603E */
|
||||||
|
#define SPR603E_DAR 19 /* 603E */
|
||||||
|
#define SPR603E_DEC 22 /* 603E */
|
||||||
|
#define SPR603E_SDR1 25 /* 603E */
|
||||||
|
#define SPR603E_TBL_R 268 /* 603E Time Base Low (Read-only) */
|
||||||
|
#define SPR603E_TBU_R 269 /* 603E Time Base High (Read-only) */
|
||||||
|
#define SPR603E_TBL_W 284 /* 603E Time Base Low (Write-only) */
|
||||||
|
#define SPR603E_TBU_W 285 /* 603E Time Base Hight (Write-only) */
|
||||||
|
#define SPR603E_EAR 282 /* 603E */
|
||||||
|
#define SPR603E_IBAT0U 528 /* 603E */
|
||||||
|
#define SPR603E_IBAT0L 529 /* 603E */
|
||||||
|
#define SPR603E_IBAT1U 530 /* 603E */
|
||||||
|
#define SPR603E_IBAT1L 531 /* 603E */
|
||||||
|
#define SPR603E_IBAT2U 532 /* 603E */
|
||||||
|
#define SPR603E_IBAT2L 533 /* 603E */
|
||||||
|
#define SPR603E_IBAT3U 534 /* 603E */
|
||||||
|
#define SPR603E_IBAT3L 535 /* 603E */
|
||||||
|
#define SPR603E_DBAT0U 536 /* 603E */
|
||||||
|
#define SPR603E_DBAT0L 537 /* 603E */
|
||||||
|
#define SPR603E_DBAT1U 538 /* 603E */
|
||||||
|
#define SPR603E_DBAT1L 539 /* 603E */
|
||||||
|
#define SPR603E_DBAT2U 540 /* 603E */
|
||||||
|
#define SPR603E_DBAT2L 541 /* 603E */
|
||||||
|
#define SPR603E_DBAT3U 542 /* 603E */
|
||||||
|
#define SPR603E_DBAT3L 543 /* 603E */
|
||||||
|
#define SPR603E_DABR 1013 /* 603E */
|
||||||
|
#define SPR603E_DMISS 0x3d0 /* 603E */
|
||||||
|
#define SPR603E_DCMP 0x3d1 /* 603E */
|
||||||
|
#define SPR603E_HASH1 0x3d2 /* 603E */
|
||||||
|
#define SPR603E_HASH2 0x3d3 /* 603E */
|
||||||
|
#define SPR603E_IMISS 0x3d4 /* 603E */
|
||||||
|
#define SPR603E_ICMP 0x3d5 /* 603E */
|
||||||
|
#define SPR603E_RPA 0x3d6 /* 603E */
|
||||||
|
#define SPR603E_HID0 1008 /* 603E */
|
||||||
|
#define SPR603E_HID1 1009 /* 603E */
|
||||||
|
#define SPR603E_IABR 1010 /* 603E */
|
||||||
|
#define SPR603E_HID2 1011 /* 603E */
|
||||||
|
|
||||||
|
#define SPR602_TCR 984 /* 602 */
|
||||||
|
#define SPR602_IBR 986 /* 602 */
|
||||||
|
#define SPR602_SP 1021 /* 602 */
|
||||||
|
#define SPR602_LT 1022 /* 602 */
|
||||||
|
|
||||||
|
|
||||||
|
#define DCR_BEAR 0x90 /* bus error address */
|
||||||
|
#define DCR_BESR 0x91 /* bus error syndrome */
|
||||||
|
#define DCR_BR0 0x80 /* bank */
|
||||||
|
#define DCR_BR1 0x81 /* bank */
|
||||||
|
#define DCR_BR2 0x82 /* bank */
|
||||||
|
#define DCR_BR3 0x83 /* bank */
|
||||||
|
#define DCR_BR4 0x84 /* bank */
|
||||||
|
#define DCR_BR5 0x85 /* bank */
|
||||||
|
#define DCR_BR6 0x86 /* bank */
|
||||||
|
#define DCR_BR7 0x87 /* bank */
|
||||||
|
#define DCR_DMACC0 0xc4 /* dma chained count */
|
||||||
|
#define DCR_DMACC1 0xcc /* dma chained count */
|
||||||
|
#define DCR_DMACC2 0xd4 /* dma chained count */
|
||||||
|
#define DCR_DMACC3 0xdc /* dma chained count */
|
||||||
|
#define DCR_DMACR0 0xc0 /* dma channel control */
|
||||||
|
#define DCR_DMACR1 0xc8 /* dma channel control */
|
||||||
|
#define DCR_DMACR2 0xd0 /* dma channel control */
|
||||||
|
#define DCR_DMACR3 0xd8 /* dma channel control */
|
||||||
|
#define DCR_DMACT0 0xc1 /* dma destination address */
|
||||||
|
#define DCR_DMACT1 0xc9 /* dma destination address */
|
||||||
|
#define DCR_DMACT2 0xd1 /* dma destination address */
|
||||||
|
#define DCR_DMACT3 0xd9 /* dma destination address */
|
||||||
|
#define DCR_DMADA0 0xc2 /* dma destination address */
|
||||||
|
#define DCR_DMADA1 0xca /* dma destination address */
|
||||||
|
#define DCR_DMADA2 0xd2 /* dma source address */
|
||||||
|
#define DCR_DMADA3 0xda /* dma source address */
|
||||||
|
#define DCR_DMASA0 0xc3 /* dma source address */
|
||||||
|
#define DCR_DMASA1 0xcb /* dma source address */
|
||||||
|
#define DCR_DMASA2 0xd3 /* dma source address */
|
||||||
|
#define DCR_DMASA3 0xdb /* dma source address */
|
||||||
|
#define DCR_DMASR 0xe0 /* dma status */
|
||||||
|
#define DCR_EXIER 0x42 /* external interrupt enable */
|
||||||
|
#define DCR_EXISR 0x40 /* external interrupt status */
|
||||||
|
#define DCR_IOCR 0xa0 /* io configuration */
|
||||||
|
|
||||||
|
enum {
|
||||||
|
EXCEPTION_IRQ = 1,
|
||||||
|
EXCEPTION_DECREMENTER = 2,
|
||||||
|
EXCEPTION_TRAP = 3,
|
||||||
|
EXCEPTION_SYSTEM_CALL = 4,
|
||||||
|
EXCEPTION_SMI = 5,
|
||||||
|
EXCEPTION_DSI = 6,
|
||||||
|
EXCEPTION_ISI = 7,
|
||||||
|
EXCEPTION_PROGRAMMABLE_INTERVAL_TIMER = 20,
|
||||||
|
EXCEPTION_FIXED_INTERVAL_TIMER = 21,
|
||||||
|
EXCEPTION_WATCHDOG_TIMER = 22,
|
||||||
|
EXCEPTION_CRITICAL_INTERRUPT = 23,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
PPC_INPUT_LINE_SMI = 10,
|
||||||
|
PPC_INPUT_LINE_TLBISYNC
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
PPC_PC=1,
|
||||||
|
PPC_MSR,
|
||||||
|
PPC_CR,
|
||||||
|
PPC_LR,
|
||||||
|
PPC_CTR,
|
||||||
|
PPC_XER,
|
||||||
|
PPC_DEC,
|
||||||
|
PPC_SRR0,
|
||||||
|
PPC_SRR1,
|
||||||
|
PPC_R0,
|
||||||
|
PPC_R1,
|
||||||
|
PPC_R2,
|
||||||
|
PPC_R3,
|
||||||
|
PPC_R4,
|
||||||
|
PPC_R5,
|
||||||
|
PPC_R6,
|
||||||
|
PPC_R7,
|
||||||
|
PPC_R8,
|
||||||
|
PPC_R9,
|
||||||
|
PPC_R10,
|
||||||
|
PPC_R11,
|
||||||
|
PPC_R12,
|
||||||
|
PPC_R13,
|
||||||
|
PPC_R14,
|
||||||
|
PPC_R15,
|
||||||
|
PPC_R16,
|
||||||
|
PPC_R17,
|
||||||
|
PPC_R18,
|
||||||
|
PPC_R19,
|
||||||
|
PPC_R20,
|
||||||
|
PPC_R21,
|
||||||
|
PPC_R22,
|
||||||
|
PPC_R23,
|
||||||
|
PPC_R24,
|
||||||
|
PPC_R25,
|
||||||
|
PPC_R26,
|
||||||
|
PPC_R27,
|
||||||
|
PPC_R28,
|
||||||
|
PPC_R29,
|
||||||
|
PPC_R30,
|
||||||
|
PPC_R31
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
PPC_MODEL_403GA = 0x00200000,
|
||||||
|
PPC_MODEL_403GB = 0x00200100,
|
||||||
|
PPC_MODEL_403GC = 0x00200200,
|
||||||
|
PPC_MODEL_403GCX = 0x00201400,
|
||||||
|
PPC_MODEL_405GP = 0x40110000,
|
||||||
|
PPC_MODEL_601 = 0x00010000,
|
||||||
|
PPC_MODEL_603 = 0x00030000, /* "Wart" */
|
||||||
|
PPC_MODEL_604 = 0x00040000, /* "Zephyr" */
|
||||||
|
PPC_MODEL_602 = 0x00050000, /* "Galahad" */
|
||||||
|
PPC_MODEL_603E = 0x00060103, /* "Stretch", version 1.3 */
|
||||||
|
PPC_MODEL_603EV = 0x00070000, /* "Valiant" */
|
||||||
|
PPC_MODEL_603R = 0x00071202, /* "Goldeneye", version 2.1 */
|
||||||
|
PPC_MODEL_740 = 0x00080301, /* "Arthur", version 3.1 */
|
||||||
|
PPC_MODEL_750 = PPC_MODEL_740,
|
||||||
|
PPC_MODEL_740P = 0x00080202, /* "Conan Doyle", version 1.2 */
|
||||||
|
PPC_MODEL_750P = PPC_MODEL_740P,
|
||||||
|
PPC_MODEL_755 = 0x00083203 /* "Goldfinger", version 2.3 */
|
||||||
|
} PPC_MODEL;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
BUS_FREQUENCY_16MHZ = 0,
|
||||||
|
BUS_FREQUENCY_20MHZ,
|
||||||
|
BUS_FREQUENCY_25MHZ,
|
||||||
|
BUS_FREQUENCY_33MHZ,
|
||||||
|
BUS_FREQUENCY_40MHZ,
|
||||||
|
BUS_FREQUENCY_50MHZ,
|
||||||
|
BUS_FREQUENCY_60MHZ,
|
||||||
|
BUS_FREQUENCY_66MHZ,
|
||||||
|
BUS_FREQUENCY_75MHZ,
|
||||||
|
} PPC_BUS_FREQUENCY;
|
||||||
|
|
||||||
|
// PLL Configuration based on the table in MPC603EUM page 7-31
|
||||||
|
static const int mpc603e_pll_config[12][9] =
|
||||||
|
{
|
||||||
|
// 16, 20, 25, 33, 40, 50, 60, 66, 75
|
||||||
|
{ -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||||
|
{ 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, -1, 0x0, -1 },
|
||||||
|
{ -1, -1, -1, -1, -1, 0xc, -1, 0xc, -1 },
|
||||||
|
{ 0x5, 0x5, 0x5, 0x4, 0x4, 0x4, -1, -1, -1 },
|
||||||
|
{ -1, -1, -1, 0x6, 0x6, -1, -1, -1, -1 },
|
||||||
|
{ -1, -1, 0x8, 0x8, -1, -1, -1, -1, -1 },
|
||||||
|
{ -1, 0xe, 0xe, -1, -1, -1, -1, -1, -1 },
|
||||||
|
{ 0xa, 0xa, 0xa, -1, -1, -1, -1, -1, -1 },
|
||||||
|
{ -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||||
|
{ -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||||
|
{ -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||||
|
{ -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
// PLL Configuration based on the table in MPC603E7VEC page 29
|
||||||
|
static const int mpc603ev_pll_config[12][9] =
|
||||||
|
{
|
||||||
|
// 16, 20, 25, 33, 40, 50, 60, 66, 75
|
||||||
|
{ -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||||
|
{ -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||||
|
{ -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||||
|
// 2:1
|
||||||
|
{ -1, -1, -1, -1, -1, -1, -1, 0x4, 0x4 },
|
||||||
|
// 2.5:1
|
||||||
|
{ -1, -1, -1, -1, -1, 0x6, 0x6, 0x6, 0x6 },
|
||||||
|
// 3:1
|
||||||
|
{ -1, -1, -1, -1, 0x8, 0x8, 0x8, 0x8, 0x8 },
|
||||||
|
// 3.5:1
|
||||||
|
{ -1, -1, -1, -1, 0xe, 0xe, 0xe, 0xe, -1 },
|
||||||
|
// 4:1
|
||||||
|
{ -1, -1, -1, 0xa, 0xa, 0xa, 0xa, -1, -1 },
|
||||||
|
// 4.5:1
|
||||||
|
{ -1, -1, -1, 0x7, 0x7, 0x7, -1, -1, -1 },
|
||||||
|
// 5:1
|
||||||
|
{ -1, -1, 0xb, 0xb, 0xb, -1, -1, -1, -1 },
|
||||||
|
// 5.5:1
|
||||||
|
{ -1, -1, 0x9, 0x9, 0x9, -1, -1, -1, -1 },
|
||||||
|
// 6:1
|
||||||
|
{ -1, -1, 0xd, 0xd, 0xd, -1, -1, -1, -1 }
|
||||||
|
};
|
||||||
|
|
||||||
|
// PLL Configuration based on the table in MPC603E7TEC page 23
|
||||||
|
static const int mpc603r_pll_config[12][9] =
|
||||||
|
{
|
||||||
|
// 16, 20, 25, 33, 40, 50, 60, 66, 75
|
||||||
|
{ -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||||
|
{ -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||||
|
{ -1, -1, -1, -1, -1, -1, -1, -1, -1 },
|
||||||
|
// 2:1
|
||||||
|
{ -1, -1, -1, -1, 0x5, 0x5, 0x5, 0x5, 0x5 },
|
||||||
|
// 2.5:1
|
||||||
|
{ -1, -1, -1, -1, -1, -1, 0x6, 0x6, 0x6 },
|
||||||
|
// 3:1
|
||||||
|
{ -1, -1, -1, -1, -1, 0x8, 0x8, 0x8, 0x8 },
|
||||||
|
// 3.5:1
|
||||||
|
{ -1, -1, -1, -1, -1, 0xe, 0xe, 0xe, 0xe },
|
||||||
|
// 4:1
|
||||||
|
{ -1, -1, -1, -1, 0xa, 0xa, 0xa, 0xa, 0xa },
|
||||||
|
// 4.5:1
|
||||||
|
{ -1, -1, -1, 0x7, 0x7, 0x7, 0x7, 0x7, -1 },
|
||||||
|
// 5:1
|
||||||
|
{ -1, -1, -1, 0xb, 0xb, 0xb, 0xb, -1, -1 },
|
||||||
|
// 5.5:1
|
||||||
|
{ -1, -1, -1, 0x9, 0x9, 0x9, -1, -1, -1 },
|
||||||
|
// 6:1
|
||||||
|
{ -1, -1, 0xd, 0xd, 0xd, 0xd, -1, -1, -1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Configuration Data Structures
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PPC_MODEL pvr;
|
||||||
|
int bus_frequency_multiplier;
|
||||||
|
PPC_BUS_FREQUENCY bus_frequency;
|
||||||
|
} PPC_CONFIG;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
UINT32 start;
|
||||||
|
UINT32 end;
|
||||||
|
UINT32 * ptr;
|
||||||
|
|
||||||
|
} PPC_FETCH_REGION;
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Functions
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
extern UINT32 ppc_get_pc(void);
|
||||||
|
extern void ppc_set_irq_line(int irqline);
|
||||||
|
extern int ppc_execute(int cycles);
|
||||||
|
extern void ppc_reset(void);
|
||||||
|
extern void ppc_shutdown(void);
|
||||||
|
extern void ppc_init(const PPC_CONFIG *config); // must be called second!
|
||||||
|
extern void ppc_set_fetch(PPC_FETCH_REGION * fetch);
|
||||||
|
|
||||||
|
// These have been added to support the new Supermodel
|
||||||
|
extern void ppc_attach_bus(class CBus *BusPtr); // must be called first!
|
||||||
|
extern void ppc_save_state(class CBlockFile *SaveState);
|
||||||
|
extern void ppc_load_state(class CBlockFile *SaveState);
|
||||||
|
extern UINT32 ppc_get_gpr(unsigned num);
|
||||||
|
extern UINT32 ppc_get_lr(void);
|
||||||
|
extern UINT32 ppc_read_spr(unsigned spr);
|
||||||
|
extern UINT32 ppc_read_sr(unsigned num);
|
||||||
|
|
||||||
|
|
||||||
|
#endif // INCLUDED_PPC_H
|
329
CPU/PowerPC/ppc603.c
Normal file
329
CPU/PowerPC/ppc603.c
Normal file
|
@ -0,0 +1,329 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ppc603.c
|
||||||
|
*
|
||||||
|
* PowerPC 603e functions. Included from ppc.cpp; do not compile separately.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void ppc603_exception(int exception)
|
||||||
|
{
|
||||||
|
switch( exception )
|
||||||
|
{
|
||||||
|
case EXCEPTION_IRQ: /* External Interrupt */
|
||||||
|
if( ppc_get_msr() & MSR_EE ) {
|
||||||
|
UINT32 msr = ppc_get_msr();
|
||||||
|
|
||||||
|
SRR0 = ppc.npc;
|
||||||
|
SRR1 = msr & 0xff73;
|
||||||
|
|
||||||
|
msr &= ~(MSR_POW | MSR_EE | MSR_PR | MSR_FP | MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1 | MSR_IR | MSR_DR | MSR_RI);
|
||||||
|
if( msr & MSR_ILE )
|
||||||
|
msr |= MSR_LE;
|
||||||
|
else
|
||||||
|
msr &= ~MSR_LE;
|
||||||
|
ppc_set_msr(msr);
|
||||||
|
|
||||||
|
if( msr & MSR_IP )
|
||||||
|
ppc.npc = 0xfff00000 | 0x0500;
|
||||||
|
else
|
||||||
|
ppc.npc = 0x00000000 | 0x0500;
|
||||||
|
|
||||||
|
ppc.interrupt_pending &= ~0x1;
|
||||||
|
ppc_change_pc(ppc.npc);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EXCEPTION_DECREMENTER: /* Decrementer overflow exception */
|
||||||
|
if( ppc_get_msr() & MSR_EE ) {
|
||||||
|
UINT32 msr = ppc_get_msr();
|
||||||
|
|
||||||
|
SRR0 = ppc.npc;
|
||||||
|
SRR1 = msr & 0xff73;
|
||||||
|
|
||||||
|
msr &= ~(MSR_POW | MSR_EE | MSR_PR | MSR_FP | MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1 | MSR_IR | MSR_DR | MSR_RI);
|
||||||
|
if( msr & MSR_ILE )
|
||||||
|
msr |= MSR_LE;
|
||||||
|
else
|
||||||
|
msr &= ~MSR_LE;
|
||||||
|
ppc_set_msr(msr);
|
||||||
|
|
||||||
|
if( msr & MSR_IP )
|
||||||
|
ppc.npc = 0xfff00000 | 0x0900;
|
||||||
|
else
|
||||||
|
ppc.npc = 0x00000000 | 0x0900;
|
||||||
|
|
||||||
|
ppc.interrupt_pending &= ~0x2;
|
||||||
|
ppc_change_pc(ppc.npc);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EXCEPTION_TRAP: /* Program exception / Trap */
|
||||||
|
{
|
||||||
|
UINT32 msr = ppc_get_msr();
|
||||||
|
|
||||||
|
SRR0 = ppc.pc;
|
||||||
|
SRR1 = (msr & 0xff73) | 0x20000; /* 0x20000 = TRAP bit */
|
||||||
|
|
||||||
|
msr &= ~(MSR_POW | MSR_EE | MSR_PR | MSR_FP | MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1 | MSR_IR | MSR_DR | MSR_RI);
|
||||||
|
if( msr & MSR_ILE )
|
||||||
|
msr |= MSR_LE;
|
||||||
|
else
|
||||||
|
msr &= ~MSR_LE;
|
||||||
|
ppc_set_msr(msr);
|
||||||
|
|
||||||
|
if( msr & MSR_IP )
|
||||||
|
ppc.npc = 0xfff00000 | 0x0700;
|
||||||
|
else
|
||||||
|
ppc.npc = 0x00000000 | 0x0700;
|
||||||
|
ppc_change_pc(ppc.npc);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EXCEPTION_SYSTEM_CALL: /* System call */
|
||||||
|
{
|
||||||
|
UINT32 msr = ppc_get_msr();
|
||||||
|
|
||||||
|
SRR0 = ppc.npc;
|
||||||
|
SRR1 = (msr & 0xff73);
|
||||||
|
|
||||||
|
msr &= ~(MSR_POW | MSR_EE | MSR_PR | MSR_FP | MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1 | MSR_IR | MSR_DR | MSR_RI);
|
||||||
|
if( msr & MSR_ILE )
|
||||||
|
msr |= MSR_LE;
|
||||||
|
else
|
||||||
|
msr &= ~MSR_LE;
|
||||||
|
ppc_set_msr(msr);
|
||||||
|
|
||||||
|
if( msr & MSR_IP )
|
||||||
|
ppc.npc = 0xfff00000 | 0x0c00;
|
||||||
|
else
|
||||||
|
ppc.npc = 0x00000000 | 0x0c00;
|
||||||
|
ppc_change_pc(ppc.npc);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EXCEPTION_SMI:
|
||||||
|
if( ppc_get_msr() & MSR_EE ) {
|
||||||
|
UINT32 msr = ppc_get_msr();
|
||||||
|
|
||||||
|
SRR0 = ppc.npc;
|
||||||
|
SRR1 = msr & 0xff73;
|
||||||
|
|
||||||
|
msr &= ~(MSR_POW | MSR_EE | MSR_PR | MSR_FP | MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1 | MSR_IR | MSR_DR | MSR_RI);
|
||||||
|
if( msr & MSR_ILE )
|
||||||
|
msr |= MSR_LE;
|
||||||
|
else
|
||||||
|
msr &= ~MSR_LE;
|
||||||
|
ppc_set_msr(msr);
|
||||||
|
|
||||||
|
if( msr & MSR_IP )
|
||||||
|
ppc.npc = 0xfff00000 | 0x1400;
|
||||||
|
else
|
||||||
|
ppc.npc = 0x00000000 | 0x1400;
|
||||||
|
|
||||||
|
ppc.interrupt_pending &= ~0x4;
|
||||||
|
ppc_change_pc(ppc.npc);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EXCEPTION_DSI:
|
||||||
|
{
|
||||||
|
UINT32 msr = ppc_get_msr();
|
||||||
|
|
||||||
|
SRR0 = ppc.npc;
|
||||||
|
SRR1 = msr & 0xff73;
|
||||||
|
|
||||||
|
msr &= ~(MSR_POW | MSR_EE | MSR_PR | MSR_FP | MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1 | MSR_IR | MSR_DR | MSR_RI);
|
||||||
|
if( msr & MSR_ILE )
|
||||||
|
msr |= MSR_LE;
|
||||||
|
else
|
||||||
|
msr &= ~MSR_LE;
|
||||||
|
ppc_set_msr(msr);
|
||||||
|
|
||||||
|
if( msr & MSR_IP )
|
||||||
|
ppc.npc = 0xfff00000 | 0x0300;
|
||||||
|
else
|
||||||
|
ppc.npc = 0x00000000 | 0x0300;
|
||||||
|
|
||||||
|
ppc.interrupt_pending &= ~0x4;
|
||||||
|
ppc_change_pc(ppc.npc);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EXCEPTION_ISI:
|
||||||
|
{
|
||||||
|
UINT32 msr = ppc_get_msr();
|
||||||
|
|
||||||
|
SRR0 = ppc.npc;
|
||||||
|
SRR1 = msr & 0xff73;
|
||||||
|
|
||||||
|
msr &= ~(MSR_POW | MSR_EE | MSR_PR | MSR_FP | MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1 | MSR_IR | MSR_DR | MSR_RI);
|
||||||
|
if( msr & MSR_ILE )
|
||||||
|
msr |= MSR_LE;
|
||||||
|
else
|
||||||
|
msr &= ~MSR_LE;
|
||||||
|
ppc_set_msr(msr);
|
||||||
|
|
||||||
|
if( msr & MSR_IP )
|
||||||
|
ppc.npc = 0xfff00000 | 0x0400;
|
||||||
|
else
|
||||||
|
ppc.npc = 0x00000000 | 0x0400;
|
||||||
|
|
||||||
|
ppc.interrupt_pending &= ~0x4;
|
||||||
|
ppc_change_pc(ppc.npc);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
ErrorLog("PowerPC triggered an unknown exception. Emulation halted until reset.");
|
||||||
|
DebugLog("PowerPC triggered an unknown exception (%d).\n", exception);
|
||||||
|
ppc.fatalError = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ppc603_set_smi_line(int state)
|
||||||
|
{
|
||||||
|
if( state ) {
|
||||||
|
ppc.interrupt_pending |= 0x4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ppc603_check_interrupts(void)
|
||||||
|
{
|
||||||
|
if (MSR & MSR_EE)
|
||||||
|
{
|
||||||
|
if (ppc.interrupt_pending != 0)
|
||||||
|
{
|
||||||
|
if (ppc.interrupt_pending & 0x1)
|
||||||
|
{
|
||||||
|
ppc603_exception(EXCEPTION_IRQ);
|
||||||
|
}
|
||||||
|
else if (ppc.interrupt_pending & 0x2)
|
||||||
|
{
|
||||||
|
ppc603_exception(EXCEPTION_DECREMENTER);
|
||||||
|
}
|
||||||
|
else if (ppc.interrupt_pending & 0x4)
|
||||||
|
{
|
||||||
|
ppc603_exception(EXCEPTION_SMI);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ppc_reset(void)
|
||||||
|
{
|
||||||
|
ppc.fatalError = FALSE; // reset the fatal error flag
|
||||||
|
|
||||||
|
ppc.pc = ppc.npc = 0xfff00100;
|
||||||
|
|
||||||
|
ppc_set_msr(0x40);
|
||||||
|
ppc_change_pc(ppc.pc);
|
||||||
|
|
||||||
|
ppc.hid0 = 1;
|
||||||
|
|
||||||
|
ppc.interrupt_pending = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ppc_execute(int cycles)
|
||||||
|
{
|
||||||
|
UINT32 opcode;
|
||||||
|
ppc_icount = cycles;
|
||||||
|
ppc_tb_base_icount = cycles;
|
||||||
|
ppc_dec_base_icount = cycles + ppc.dec_frac;
|
||||||
|
|
||||||
|
// check if decrementer exception occurs during execution
|
||||||
|
if ((UINT32)(DEC - (cycles / (bus_freq_multiplier * 2))) > (UINT32)(DEC))
|
||||||
|
{
|
||||||
|
ppc_dec_trigger_cycle = ((cycles / (bus_freq_multiplier * 2)) - DEC) * 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ppc_dec_trigger_cycle = 0x7fffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
ppc_change_pc(ppc.npc);
|
||||||
|
|
||||||
|
/*{
|
||||||
|
char string1[200];
|
||||||
|
char string2[200];
|
||||||
|
opcode = BSWAP32(*ppc.op);
|
||||||
|
DisassemblePowerPC(opcode, ppc.npc, string1, string2, TRUE);
|
||||||
|
printf("%08X: %s %s\n", ppc.npc, string1, string2);
|
||||||
|
}*/
|
||||||
|
// printf("trigger cycle %d (%08X)\n", ppc_dec_trigger_cycle, ppc_dec_trigger_cycle);
|
||||||
|
// printf("tb = %08X %08X\n", (UINT32)(ppc.tb >> 32), (UINT32)(ppc.tb));
|
||||||
|
|
||||||
|
ppc603_check_interrupts();
|
||||||
|
|
||||||
|
while( ppc_icount > 0 && !ppc.fatalError)
|
||||||
|
{
|
||||||
|
ppc.pc = ppc.npc;
|
||||||
|
|
||||||
|
//if (ppc.pc == 0x279C)
|
||||||
|
// printf("R3=%08X\n", REG(3));
|
||||||
|
|
||||||
|
opcode = *ppc.op++; // Supermodel byte reverses each aligned word (converting them to little endian) so they can be fetched directly
|
||||||
|
//opcode = BSWAP32(*ppc.op++);
|
||||||
|
|
||||||
|
ppc.npc = ppc.pc + 4;
|
||||||
|
switch(opcode >> 26)
|
||||||
|
{
|
||||||
|
case 19: optable19[(opcode >> 1) & 0x3ff](opcode); break;
|
||||||
|
case 31: optable31[(opcode >> 1) & 0x3ff](opcode); break;
|
||||||
|
case 59: optable59[(opcode >> 1) & 0x3ff](opcode); break;
|
||||||
|
case 63: optable63[(opcode >> 1) & 0x3ff](opcode); break;
|
||||||
|
default: optable[opcode >> 26](opcode); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ppc_icount--;
|
||||||
|
|
||||||
|
if(ppc_icount == ppc_dec_trigger_cycle)
|
||||||
|
{
|
||||||
|
// printf("dec int at %d\n", ppc_icount);
|
||||||
|
ppc.interrupt_pending |= 0x2;
|
||||||
|
ppc603_check_interrupts();
|
||||||
|
}
|
||||||
|
|
||||||
|
//ppc603_check_interrupts();
|
||||||
|
}
|
||||||
|
|
||||||
|
// update timebase
|
||||||
|
// timebase is incremented once every four core clock cycles, so adjust the cycles accordingly
|
||||||
|
ppc.tb += ((ppc_tb_base_icount - ppc_icount) / 4);
|
||||||
|
|
||||||
|
// update decrementer
|
||||||
|
ppc.dec_frac = ((ppc_dec_base_icount - ppc_icount) % (bus_freq_multiplier * 2));
|
||||||
|
DEC -= ((ppc_dec_base_icount - ppc_icount) / (bus_freq_multiplier * 2));
|
||||||
|
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
char string1[200];
|
||||||
|
char string2[200];
|
||||||
|
opcode = BSWAP32(*ppc.op);
|
||||||
|
DisassemblePowerPC(opcode, ppc.npc, string1, string2, TRUE);
|
||||||
|
printf("%08X: %s %s\n", ppc.npc, string1, string2);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
return cycles - ppc_icount;
|
||||||
|
}
|
2832
CPU/PowerPC/ppc_ops.c
Normal file
2832
CPU/PowerPC/ppc_ops.c
Normal file
File diff suppressed because it is too large
Load diff
170
CPU/PowerPC/ppc_ops.h
Normal file
170
CPU/PowerPC/ppc_ops.h
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ppc_ops.h
|
||||||
|
*
|
||||||
|
* Header file defining PowerPC opcode handlers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static PPC_OPCODE ppc_opcode_common[] =
|
||||||
|
{
|
||||||
|
/*code subcode handler */
|
||||||
|
{ 31, 266, ppc_addx },
|
||||||
|
{ 31, 266 | 512, ppc_addx },
|
||||||
|
{ 31, 10, ppc_addcx },
|
||||||
|
{ 31, 10 | 512, ppc_addcx },
|
||||||
|
{ 31, 138, ppc_addex },
|
||||||
|
{ 31, 138 | 512, ppc_addex },
|
||||||
|
{ 14, -1, ppc_addi },
|
||||||
|
{ 12, -1, ppc_addic },
|
||||||
|
{ 13, -1, ppc_addic_rc },
|
||||||
|
{ 15, -1, ppc_addis },
|
||||||
|
{ 31, 234, ppc_addmex },
|
||||||
|
{ 31, 234 | 512, ppc_addmex },
|
||||||
|
{ 31, 202, ppc_addzex },
|
||||||
|
{ 31, 202 | 512, ppc_addzex },
|
||||||
|
{ 31, 28, ppc_andx },
|
||||||
|
{ 31, 28 | 512, ppc_andx },
|
||||||
|
{ 31, 60, ppc_andcx },
|
||||||
|
{ 28, -1, ppc_andi_rc },
|
||||||
|
{ 29, -1, ppc_andis_rc },
|
||||||
|
{ 18, -1, ppc_bx },
|
||||||
|
{ 16, -1, ppc_bcx },
|
||||||
|
{ 19, 528, ppc_bcctrx },
|
||||||
|
{ 19, 16, ppc_bclrx },
|
||||||
|
{ 31, 0, ppc_cmp },
|
||||||
|
{ 11, -1, ppc_cmpi },
|
||||||
|
{ 31, 32, ppc_cmpl },
|
||||||
|
{ 10, -1, ppc_cmpli },
|
||||||
|
{ 31, 26, ppc_cntlzw },
|
||||||
|
{ 19, 257, ppc_crand },
|
||||||
|
{ 19, 129, ppc_crandc },
|
||||||
|
{ 19, 289, ppc_creqv },
|
||||||
|
{ 19, 225, ppc_crnand },
|
||||||
|
{ 19, 33, ppc_crnor },
|
||||||
|
{ 19, 449, ppc_cror },
|
||||||
|
{ 19, 417, ppc_crorc },
|
||||||
|
{ 19, 193, ppc_crxor },
|
||||||
|
{ 31, 86, ppc_dcbf },
|
||||||
|
{ 31, 470, ppc_dcbi },
|
||||||
|
{ 31, 54, ppc_dcbst },
|
||||||
|
{ 31, 278, ppc_dcbt },
|
||||||
|
{ 31, 246, ppc_dcbtst },
|
||||||
|
{ 31, 1014, ppc_dcbz },
|
||||||
|
{ 31, 491, ppc_divwx },
|
||||||
|
{ 31, 491 | 512, ppc_divwx },
|
||||||
|
{ 31, 459, ppc_divwux },
|
||||||
|
{ 31, 459 | 512, ppc_divwux },
|
||||||
|
{ 31, 854, ppc_eieio },
|
||||||
|
{ 31, 284, ppc_eqvx },
|
||||||
|
{ 31, 954, ppc_extsbx },
|
||||||
|
{ 31, 922, ppc_extshx },
|
||||||
|
{ 31, 982, ppc_icbi },
|
||||||
|
{ 19, 150, ppc_isync },
|
||||||
|
{ 34, -1, ppc_lbz },
|
||||||
|
{ 35, -1, ppc_lbzu },
|
||||||
|
{ 31, 119, ppc_lbzux },
|
||||||
|
{ 31, 87, ppc_lbzx },
|
||||||
|
{ 42, -1, ppc_lha },
|
||||||
|
{ 43, -1, ppc_lhau },
|
||||||
|
{ 31, 375, ppc_lhaux },
|
||||||
|
{ 31, 343, ppc_lhax },
|
||||||
|
{ 31, 790, ppc_lhbrx },
|
||||||
|
{ 40, -1, ppc_lhz },
|
||||||
|
{ 41, -1, ppc_lhzu },
|
||||||
|
{ 31, 311, ppc_lhzux },
|
||||||
|
{ 31, 279, ppc_lhzx },
|
||||||
|
{ 46, -1, ppc_lmw },
|
||||||
|
{ 31, 597, ppc_lswi },
|
||||||
|
{ 31, 533, ppc_lswx },
|
||||||
|
{ 31, 20, ppc_lwarx },
|
||||||
|
{ 31, 534, ppc_lwbrx },
|
||||||
|
{ 32, -1, ppc_lwz },
|
||||||
|
{ 33, -1, ppc_lwzu },
|
||||||
|
{ 31, 55, ppc_lwzux },
|
||||||
|
{ 31, 23, ppc_lwzx },
|
||||||
|
{ 19, 0, ppc_mcrf },
|
||||||
|
{ 31, 512, ppc_mcrxr },
|
||||||
|
{ 31, 19, ppc_mfcr },
|
||||||
|
{ 31, 83, ppc_mfmsr },
|
||||||
|
{ 31, 339, ppc_mfspr },
|
||||||
|
{ 31, 144, ppc_mtcrf },
|
||||||
|
{ 31, 146, ppc_mtmsr },
|
||||||
|
{ 31, 467, ppc_mtspr },
|
||||||
|
{ 31, 75, ppc_mulhwx },
|
||||||
|
{ 31, 11, ppc_mulhwux },
|
||||||
|
{ 7, -1, ppc_mulli },
|
||||||
|
{ 31, 235, ppc_mullwx },
|
||||||
|
{ 31, 235 | 512, ppc_mullwx },
|
||||||
|
{ 31, 476, ppc_nandx },
|
||||||
|
{ 31, 104, ppc_negx },
|
||||||
|
{ 31, 104 | 512, ppc_negx },
|
||||||
|
{ 31, 124, ppc_norx },
|
||||||
|
{ 31, 444, ppc_orx },
|
||||||
|
{ 31, 412, ppc_orcx },
|
||||||
|
{ 24, -1, ppc_ori },
|
||||||
|
{ 25, -1, ppc_oris },
|
||||||
|
{ 19, 50, ppc_rfi },
|
||||||
|
{ 20, -1, ppc_rlwimix },
|
||||||
|
{ 21, -1, ppc_rlwinmx },
|
||||||
|
{ 23, -1, ppc_rlwnmx },
|
||||||
|
{ 17, -1, ppc_sc },
|
||||||
|
{ 31, 24, ppc_slwx },
|
||||||
|
{ 31, 792, ppc_srawx },
|
||||||
|
{ 31, 824, ppc_srawix },
|
||||||
|
{ 31, 536, ppc_srwx },
|
||||||
|
{ 38, -1, ppc_stb },
|
||||||
|
{ 39, -1, ppc_stbu },
|
||||||
|
{ 31, 247, ppc_stbux },
|
||||||
|
{ 31, 215, ppc_stbx },
|
||||||
|
{ 44, -1, ppc_sth },
|
||||||
|
{ 31, 918, ppc_sthbrx },
|
||||||
|
{ 45, -1, ppc_sthu },
|
||||||
|
{ 31, 439, ppc_sthux },
|
||||||
|
{ 31, 407, ppc_sthx },
|
||||||
|
{ 47, -1, ppc_stmw },
|
||||||
|
{ 31, 725, ppc_stswi },
|
||||||
|
{ 31, 661, ppc_stswx },
|
||||||
|
{ 36, -1, ppc_stw },
|
||||||
|
{ 31, 662, ppc_stwbrx },
|
||||||
|
{ 31, 150, ppc_stwcx_rc },
|
||||||
|
{ 37, -1, ppc_stwu },
|
||||||
|
{ 31, 183, ppc_stwux },
|
||||||
|
{ 31, 151, ppc_stwx },
|
||||||
|
{ 31, 40, ppc_subfx },
|
||||||
|
{ 31, 40 | 512, ppc_subfx },
|
||||||
|
{ 31, 8, ppc_subfcx },
|
||||||
|
{ 31, 8 | 512, ppc_subfcx },
|
||||||
|
{ 31, 136, ppc_subfex },
|
||||||
|
{ 31, 136 | 512, ppc_subfex },
|
||||||
|
{ 8, -1, ppc_subfic },
|
||||||
|
{ 31, 232, ppc_subfmex },
|
||||||
|
{ 31, 232 | 512, ppc_subfmex },
|
||||||
|
{ 31, 200, ppc_subfzex },
|
||||||
|
{ 31, 200 | 512, ppc_subfzex },
|
||||||
|
{ 31, 598, ppc_sync },
|
||||||
|
{ 31, 4, ppc_tw },
|
||||||
|
{ 3, -1, ppc_twi },
|
||||||
|
{ 31, 316, ppc_xorx },
|
||||||
|
{ 26, -1, ppc_xori },
|
||||||
|
{ 27, -1, ppc_xoris }
|
||||||
|
};
|
94
Games.h
Normal file
94
Games.h
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Games.h
|
||||||
|
*
|
||||||
|
* Header file containing Model 3 game and ROM file information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDED_GAMES_H
|
||||||
|
#define INCLUDED_GAMES_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "ROMLoad.h" // ROMInfo structure
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Definitions
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
// Input flags
|
||||||
|
#define GAME_INPUT_COMMON 0x0001 // game has common controls
|
||||||
|
#define GAME_INPUT_VEHICLE 0x0002 // game has vehicle controls
|
||||||
|
#define GAME_INPUT_JOYSTICK1 0x0004 // game has joystick 1
|
||||||
|
#define GAME_INPUT_JOYSTICK2 0x0008 // game has joystick 2
|
||||||
|
#define GAME_INPUT_FIGHTING 0x0010 // game has fighting game controls
|
||||||
|
#define GAME_INPUT_VR 0x0020 // game has VR view buttons
|
||||||
|
#define GAME_INPUT_RALLY 0x0040 // game has rally car controls
|
||||||
|
#define GAME_INPUT_GUN1 0x0080 // game has gun 1
|
||||||
|
#define GAME_INPUT_GUN2 0x0100 // game has gun 2
|
||||||
|
#define GAME_INPUT_SHIFT4 0x0200 // game has 4-speed shifter
|
||||||
|
#define GAME_INPUT_ANALOG_JOYSTICK 0x0400 // game has analog joystick
|
||||||
|
#define GAME_INPUT_TWIN_JOYSTICKS 0x0800 // game has twin joysticks
|
||||||
|
#define GAME_INPUT_SOCCER 0x1000 // game has soccer controls
|
||||||
|
#define GAME_INPUT_ALL 0x1FFF
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Data Structures
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GameInfo:
|
||||||
|
*
|
||||||
|
* Describes a Model 3 game. List is terminated when title == NULL.
|
||||||
|
*/
|
||||||
|
struct GameInfo
|
||||||
|
{
|
||||||
|
// Game information
|
||||||
|
const char id[9]; // 8-character game identifier (also serves as zip archive file name)
|
||||||
|
const char *title; // complete game title
|
||||||
|
const char *mfgName; // name of manufacturer
|
||||||
|
unsigned year; // year released (in decimal)
|
||||||
|
int step; // Model 3 hardware stepping: 0x10 = 1.0, 0x15 = 1.5, 0x20 = 2.0, 0x21 = 2.1
|
||||||
|
unsigned cromSize; // size of fixed CROM (up to 8 MB)
|
||||||
|
BOOL mirrorLow64MB; // mirror low 64 MB of banked CROM space to upper 64 MB
|
||||||
|
unsigned vromSize; // size of video ROMs (32 or 64 MB; if the latter, will have to be mirrored)
|
||||||
|
unsigned inputFlags; // game input types
|
||||||
|
|
||||||
|
// ROM files
|
||||||
|
struct ROMInfo ROM[40];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Model 3 Game List
|
||||||
|
|
||||||
|
All games supported by Supermodel. All ROMs are loaded according to their
|
||||||
|
native endianness. That is, the PowerPC ROMs are loaded just as a real
|
||||||
|
PowerPC would see them. The emulator may reorder the bytes on its own for
|
||||||
|
performance reasons (but the ROMs are not specified that way here).
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
extern const struct GameInfo Model3GameList[];
|
||||||
|
|
||||||
|
|
||||||
|
#endif // INCLUDED_GAMES_H
|
64
Graphics/Error.cpp
Normal file
64
Graphics/Error.cpp
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Error.cpp
|
||||||
|
*
|
||||||
|
* Error reporting for 3D renderer. There are some situations in which it is
|
||||||
|
* appropriate to inform the user of an error during rendering. However, these
|
||||||
|
* will frequently lead to an avalanche of error messages. Therefore, error
|
||||||
|
* messages are managed by this interface, ensuring that they are displayed not
|
||||||
|
* more than once per frame.
|
||||||
|
*
|
||||||
|
* Error functions can always be counted on to return FAIL, like ErrorLog().
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Supermodel.h"
|
||||||
|
|
||||||
|
|
||||||
|
// Error bit flags (must not conflict)
|
||||||
|
#define ERROR_LOCAL_VERTEX_OVERFLOW 0x1
|
||||||
|
#define ERROR_UNABLE_TO_CACHE_MODEL 0x2
|
||||||
|
|
||||||
|
|
||||||
|
// Overflow in the local vertex buffer, which holds one model
|
||||||
|
BOOL CRender3D::ErrorLocalVertexOverflow(void)
|
||||||
|
{
|
||||||
|
if ((errorMsgFlags&ERROR_LOCAL_VERTEX_OVERFLOW))
|
||||||
|
return FAIL;
|
||||||
|
errorMsgFlags |= ERROR_LOCAL_VERTEX_OVERFLOW;
|
||||||
|
return ErrorLog("Overflow in local vertex buffer!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Model could not be cached, even after dumping display list and re-caching
|
||||||
|
BOOL CRender3D::ErrorUnableToCacheModel(UINT32 modelAddr)
|
||||||
|
{
|
||||||
|
if ((errorMsgFlags&ERROR_UNABLE_TO_CACHE_MODEL))
|
||||||
|
return FAIL;
|
||||||
|
errorMsgFlags |= ERROR_UNABLE_TO_CACHE_MODEL;
|
||||||
|
return ErrorLog("Encountered a model that was too large to cache (at %08X)!", modelAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call this every frame to clear the error flag, allowing errors to be printed
|
||||||
|
void CRender3D::ClearErrors(void)
|
||||||
|
{
|
||||||
|
errorMsgFlags = 0;
|
||||||
|
}
|
809
Graphics/Models.cpp
Normal file
809
Graphics/Models.cpp
Normal file
|
@ -0,0 +1,809 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Models.cpp
|
||||||
|
*
|
||||||
|
* Model parsing, caching, and drawing.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "Supermodel.h"
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Definitions and Constants
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* VBO Vertex Layout
|
||||||
|
*
|
||||||
|
* All vertex information is stored in an array of GLfloats. Offset and size
|
||||||
|
* information is defined here for now.
|
||||||
|
*/
|
||||||
|
#define VBO_VERTEX_OFFSET_X 0 // vertex X *
|
||||||
|
#define VBO_VERTEX_OFFSET_Y 1 // vertex Y *
|
||||||
|
#define VBO_VERTEX_OFFSET_Z 2 // vertex Z *
|
||||||
|
#define VBO_VERTEX_OFFSET_NX 3 // normal X *
|
||||||
|
#define VBO_VERTEX_OFFSET_NY 4 // normal Y *
|
||||||
|
#define VBO_VERTEX_OFFSET_NZ 5 // normal Z *
|
||||||
|
#define VBO_VERTEX_OFFSET_R 6 // color (untextured polys) and material (textured polys) R c (w/ texenable?)
|
||||||
|
#define VBO_VERTEX_OFFSET_G 7 // color and material G
|
||||||
|
#define VBO_VERTEX_OFFSET_B 8 // color and material B
|
||||||
|
#define VBO_VERTEX_OFFSET_TRANSLUCENCE 9 // translucence level (0.0 fully transparent, 1.0 opaque) *
|
||||||
|
#define VBO_VERTEX_OFFSET_LIGHTENABLE 10 // lighting enabled (0.0 luminous, 1.0 light enabled) *
|
||||||
|
#define VBO_VERTEX_OFFSET_FOGINTENSITY 11 // fog intensity (0.0 no fog applied, 1.0 all fog applied) * (combine w/ lightenable, making one negative)
|
||||||
|
#define VBO_VERTEX_OFFSET_U 12 // texture U coordinate (in texels, relative to sub-texture) *
|
||||||
|
#define VBO_VERTEX_OFFSET_V 13 // texture V coordinate *
|
||||||
|
#define VBO_VERTEX_OFFSET_TEXTURE_X 14 // sub-texture parameters, X (position in overall texture map, in texels) *
|
||||||
|
#define VBO_VERTEX_OFFSET_TEXTURE_Y 15 // "" Y "" *
|
||||||
|
#define VBO_VERTEX_OFFSET_TEXTURE_W 16 // sub-texture parameters, width of texture in texels *
|
||||||
|
#define VBO_VERTEX_OFFSET_TEXTURE_H 17 // "" height of texture in texels *
|
||||||
|
#define VBO_VERTEX_OFFSET_TEXPARAMS_EN 18 // texture parameter: ==1 texturing enabled, ==0 disabled (per-polygon) c (w/ R?)
|
||||||
|
#define VBO_VERTEX_OFFSET_TEXPARAMS_TRANS 19 // texture parameter: >=0 use transparency bit, <0 no transparency (per-polygon) c (w/ contour?)
|
||||||
|
#define VBO_VERTEX_OFFSET_TEXPARAMS_UWRAP 20 // texture parameters: U wrap mode: ==1 mirrored repeat, ==0 normal repeat
|
||||||
|
#define VBO_VERTEX_OFFSET_TEXPARAMS_VWRAP 21 // "" V wrap mode ""
|
||||||
|
#define VBO_VERTEX_OFFSET_TEXFORMAT_CONTOUR 22 // contour texture: >0 indicates contour texture (see also texParams.trans) c (w/ trans?)
|
||||||
|
#define VBO_VERTEX_SIZE 23 // total size (may include padding for alignment)
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Display Lists
|
||||||
|
|
||||||
|
Every instance of a model encountered in the scene database during rendering
|
||||||
|
is stored in the display list along with its current transformation matrices
|
||||||
|
and other state information. Display lists are bound to model caches for
|
||||||
|
performance: only one VBO has to be bound for an entire display list.
|
||||||
|
|
||||||
|
Binding display lists to model caches may cause priority problems among
|
||||||
|
alpha polygons. Therefore, it may be necessary in the future to decouple them.
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
// Draws the display list
|
||||||
|
void CRender3D::DrawDisplayList(ModelCache *Cache, POLY_STATE state)
|
||||||
|
{
|
||||||
|
DisplayList *D;
|
||||||
|
|
||||||
|
// Bind and activate VBO (pointers activate currently bound VBO)
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, Cache->vboID);
|
||||||
|
glVertexPointer(3, GL_FLOAT, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_X*sizeof(GLfloat)));
|
||||||
|
glNormalPointer(GL_FLOAT, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_NX*sizeof(GLfloat)));
|
||||||
|
glTexCoordPointer(2, GL_FLOAT, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_U*sizeof(GLfloat)));
|
||||||
|
glColorPointer(3, GL_FLOAT, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_R*sizeof(GLfloat)));
|
||||||
|
glVertexAttribPointer(subTextureLoc, 4, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TEXTURE_X*sizeof(GLfloat)));
|
||||||
|
glVertexAttribPointer(texParamsLoc, 4, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TEXPARAMS_EN*sizeof(GLfloat)));
|
||||||
|
glVertexAttribPointer(texFormatLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TEXFORMAT_CONTOUR*sizeof(GLfloat)));
|
||||||
|
glVertexAttribPointer(transLevelLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_TRANSLUCENCE*sizeof(GLfloat)));
|
||||||
|
glVertexAttribPointer(lightEnableLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_LIGHTENABLE*sizeof(GLfloat)));
|
||||||
|
glVertexAttribPointer(fogIntensityLoc, 1, GL_FLOAT, GL_FALSE, VBO_VERTEX_SIZE*sizeof(GLfloat), (GLvoid *) (VBO_VERTEX_OFFSET_FOGINTENSITY*sizeof(GLfloat)));
|
||||||
|
|
||||||
|
// Set up state
|
||||||
|
if (state == POLY_STATE_ALPHA)
|
||||||
|
{
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
glDisable(GL_BLEND);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw if there are items in the list
|
||||||
|
D = Cache->ListHead[state];
|
||||||
|
while (D != NULL)
|
||||||
|
{
|
||||||
|
if (D->isViewport)
|
||||||
|
{
|
||||||
|
if (D->next != NULL) // if nothing follows, no point in doing this
|
||||||
|
{
|
||||||
|
if (!D->next->isViewport)
|
||||||
|
{
|
||||||
|
glUniform3fv(lightingLoc, 2, D->Data.Viewport.lightingParams);
|
||||||
|
glUniformMatrix4fv(projectionMatrixLoc, 1, GL_FALSE, D->Data.Viewport.projectionMatrix);
|
||||||
|
glFogf(GL_FOG_DENSITY, D->Data.Viewport.fogParams[3]);
|
||||||
|
glFogf(GL_FOG_START, D->Data.Viewport.fogParams[4]);
|
||||||
|
glFogfv(GL_FOG_COLOR, &(D->Data.Viewport.fogParams[0]));
|
||||||
|
glUniform4fv(spotEllipseLoc, 1, D->Data.Viewport.spotEllipse);
|
||||||
|
glUniform2fv(spotRangeLoc, 1, D->Data.Viewport.spotRange);
|
||||||
|
glUniform3fv(spotColorLoc, 1, D->Data.Viewport.spotColor);
|
||||||
|
glViewport(D->Data.Viewport.x, D->Data.Viewport.y, D->Data.Viewport.width, D->Data.Viewport.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
glUniformMatrix4fv(modelViewMatrixLoc, 1, GL_FALSE, D->Data.Model.modelViewMatrix);
|
||||||
|
glDrawArrays(GL_TRIANGLES, D->Data.Model.index, D->Data.Model.numVerts);
|
||||||
|
}
|
||||||
|
|
||||||
|
D = D->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Appends an instance of a model or viewport to the display list, copying over the required state information
|
||||||
|
BOOL CRender3D::AppendDisplayList(ModelCache *Cache, BOOL isViewport, int modelNum)
|
||||||
|
{
|
||||||
|
int lm, i;
|
||||||
|
|
||||||
|
if ((Cache->listSize+2) > Cache->maxListSize) // a model may have 2 states (viewports are added to both display lists)
|
||||||
|
return FAIL;
|
||||||
|
//return ErrorLog("Display list is full.");
|
||||||
|
|
||||||
|
// Insert states into the display list
|
||||||
|
for (i = 0; i < 2; i++)
|
||||||
|
{
|
||||||
|
if (isViewport)
|
||||||
|
{
|
||||||
|
// Get index for new display list item and advance to next one
|
||||||
|
lm = Cache->listSize++;
|
||||||
|
|
||||||
|
// Viewport parameters
|
||||||
|
Cache->List[lm].Data.Viewport.x = viewportX;
|
||||||
|
Cache->List[lm].Data.Viewport.y = viewportY;
|
||||||
|
Cache->List[lm].Data.Viewport.width = viewportWidth;
|
||||||
|
Cache->List[lm].Data.Viewport.height = viewportHeight;
|
||||||
|
|
||||||
|
// Copy over lighting and fog state
|
||||||
|
memcpy(Cache->List[lm].Data.Viewport.lightingParams, lightingParams, sizeof(lightingParams));
|
||||||
|
memcpy(Cache->List[lm].Data.Viewport.fogParams, fogParams, sizeof(fogParams));
|
||||||
|
memcpy(Cache->List[lm].Data.Viewport.spotEllipse, spotEllipse, sizeof(spotEllipse));
|
||||||
|
memcpy(Cache->List[lm].Data.Viewport.spotRange, spotRange, sizeof(spotRange));
|
||||||
|
memcpy(Cache->List[lm].Data.Viewport.spotColor, spotColor, sizeof(spotColor));
|
||||||
|
|
||||||
|
// Copy projection matrix
|
||||||
|
glGetFloatv(GL_PROJECTION_MATRIX, Cache->List[lm].Data.Viewport.projectionMatrix);
|
||||||
|
}
|
||||||
|
else if (Cache->Models[modelNum].numVerts[i] > 0) // vertices exist for this state
|
||||||
|
{
|
||||||
|
// Get index for new display list item and advance to next one
|
||||||
|
lm = Cache->listSize++;
|
||||||
|
|
||||||
|
// Point to VBO for current model and state
|
||||||
|
Cache->List[lm].Data.Model.index = Cache->Models[modelNum].index[i];
|
||||||
|
Cache->List[lm].Data.Model.numVerts = Cache->Models[modelNum].numVerts[i];
|
||||||
|
|
||||||
|
// Copy modelview matrix
|
||||||
|
glGetFloatv(GL_MODELVIEW_MATRIX, Cache->List[lm].Data.Model.modelViewMatrix);
|
||||||
|
}
|
||||||
|
else // nothing to do, continue loop
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Update list pointers and set list node type
|
||||||
|
Cache->List[lm].isViewport = isViewport;
|
||||||
|
Cache->List[lm].next = NULL; // current end of list
|
||||||
|
if (Cache->ListHead[i] == NULL)
|
||||||
|
{
|
||||||
|
Cache->ListHead[i] = &(Cache->List[lm]);
|
||||||
|
Cache->ListTail[i] = Cache->ListHead[i];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Cache->ListTail[i]->next = &(Cache->List[lm]);
|
||||||
|
Cache->ListTail[i] = &(Cache->List[lm]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return OKAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clears the display list in preparation for a new frame
|
||||||
|
void CRender3D::ClearDisplayList(ModelCache *Cache)
|
||||||
|
{
|
||||||
|
Cache->listSize = 0;
|
||||||
|
for (int i = 0; i < 2; i++)
|
||||||
|
{
|
||||||
|
Cache->ListHead[i] = NULL;
|
||||||
|
Cache->ListTail[i] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Model Caching
|
||||||
|
|
||||||
|
Note that as vertices are inserted into the appropriate local vertex buffer
|
||||||
|
(sorted by polygon state -- alpha and normal), the VBO index is advanced to
|
||||||
|
reserve space and does not correspond to the actual position of each vertex.
|
||||||
|
Vertices are copied in batches sorted by state when the model is complete.
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
static void CrossProd(GLfloat out[3], GLfloat a[3], GLfloat b[3])
|
||||||
|
{
|
||||||
|
out[0] = a[1]*b[2]-a[2]*b[1];
|
||||||
|
out[1] = a[2]*b[0]-a[0]*b[2];
|
||||||
|
out[2] = a[0]*b[1]-a[1]*b[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inserts a vertex into the local vertex buffer, incrementing both the local and VBO pointers. The normal is scaled by normFlip.
|
||||||
|
void CRender3D::InsertVertex(ModelCache *Cache, const Vertex *V, const Poly *P, float normFlip)
|
||||||
|
{
|
||||||
|
GLfloat r, g, b;
|
||||||
|
GLfloat translucence, fogIntensity, texWidth, texHeight, texBaseX, texBaseY, contourProcessing;
|
||||||
|
unsigned baseIdx, texFormat, texEnable, lightEnable, modulate, colorIdx;
|
||||||
|
int s, texPage;
|
||||||
|
|
||||||
|
// Texture selection
|
||||||
|
texEnable = P->header[6]&0x04000000;
|
||||||
|
texFormat = (P->header[6]>>7)&7;
|
||||||
|
texWidth = (GLfloat) (32<<((P->header[3]>>3)&7));
|
||||||
|
texHeight = (GLfloat) (32<<((P->header[3]>>0)&7));
|
||||||
|
texPage = (P->header[4]&0x40) ? 1024 : 0; // treat texture page as Y coordinate
|
||||||
|
texBaseX = (GLfloat) (32*(((P->header[4]&0x1F)<<1)|((P->header[5]>>7)&1)));
|
||||||
|
texBaseY = (GLfloat) (32*(P->header[5]&0x1F)+texPage);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Lighting and Color Modulation:
|
||||||
|
*
|
||||||
|
* It appears that there is a modulate bit which causes the polygon color
|
||||||
|
* to be multiplied by texel colors. However, if polygons are luminous,
|
||||||
|
* this appears to be disabled (not quite correct yet, though).
|
||||||
|
*/
|
||||||
|
|
||||||
|
lightEnable = !(P->header[6]&0x00010000);
|
||||||
|
modulate = !(P->header[4]&0x80);
|
||||||
|
modulate = P->header[3]&0x80; // seems to work better
|
||||||
|
|
||||||
|
|
||||||
|
// Material color
|
||||||
|
if ((P->header[1]&2) == 0)
|
||||||
|
{
|
||||||
|
colorIdx = (P->header[4]>>20)&0x7FF;
|
||||||
|
b = (GLfloat) (polyRAM[0x400+colorIdx]&0xFF) * (1.0f/255.0f);
|
||||||
|
g = (GLfloat) ((polyRAM[0x400+colorIdx]>>8)&0xFF) * (1.0f/255.0f);
|
||||||
|
r = (GLfloat) ((polyRAM[0x400+colorIdx]>>16)&0xFF) * (1.0f/255.0f);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Colors are 8-bit (almost certainly true, see Star Wars)
|
||||||
|
r = (GLfloat) (P->header[4]>>24) * (1.0f/255.0f);
|
||||||
|
g = (GLfloat) ((P->header[4]>>16)&0xFF) * (1.0f/255.0f);
|
||||||
|
b = (GLfloat) ((P->header[4]>>8)&0xFF) * (1.0f/255.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine modulation settings
|
||||||
|
if (texEnable)
|
||||||
|
{
|
||||||
|
//if (!lightEnable|| !modulate)
|
||||||
|
if (!modulate)
|
||||||
|
r = g = b = 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
if (texFormat==5)//texFormat==6||texFormat==2)
|
||||||
|
{
|
||||||
|
//printf("%03X\n", P->header[4]>>8);
|
||||||
|
//texEnable=0;
|
||||||
|
g=b=1.0;
|
||||||
|
r=1.0f;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if 0
|
||||||
|
if ((P->header[testWord]&(1<<testBit)))
|
||||||
|
{
|
||||||
|
texEnable = 0;
|
||||||
|
r=b=0;
|
||||||
|
g=1.0f;
|
||||||
|
if (!lightEnable)
|
||||||
|
b=1.0f;
|
||||||
|
lightEnable=0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Determine whether polygon is translucent
|
||||||
|
translucence = (GLfloat) ((P->header[6]>>18)&0x1F) * (1.0f/31.0f);
|
||||||
|
if ((P->header[6]&0x00800000)) // if set, polygon is opaque
|
||||||
|
translucence = 1.0f;
|
||||||
|
|
||||||
|
// Fog intensity (for luminous polygons)
|
||||||
|
fogIntensity = (GLfloat) ((P->header[6]>>11)&0x1F) * (1.0f/31.0f);
|
||||||
|
if (!(P->header[6]&0x00010000)) // if not luminous, always use full fog intensity
|
||||||
|
fogIntensity = 1.0f;
|
||||||
|
|
||||||
|
// Contour processing
|
||||||
|
if ((P->header[6]&0x80000000) || (texFormat==7)) // contour processing enabled or alpha texture
|
||||||
|
contourProcessing = 1.0f;
|
||||||
|
else
|
||||||
|
contourProcessing = -1.0f;
|
||||||
|
|
||||||
|
// Store to local vertex buffer
|
||||||
|
s = P->state;
|
||||||
|
baseIdx = Cache->curVertIdx[s]*VBO_VERTEX_SIZE;
|
||||||
|
|
||||||
|
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_X] = V->x;
|
||||||
|
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_Y] = V->y;
|
||||||
|
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_Z] = V->z;
|
||||||
|
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_R] = r;
|
||||||
|
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_G] = g;
|
||||||
|
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_B] = b;
|
||||||
|
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TRANSLUCENCE] = translucence;
|
||||||
|
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_LIGHTENABLE] = lightEnable ? 1.0f : 0.0f;
|
||||||
|
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_FOGINTENSITY] = fogIntensity;
|
||||||
|
|
||||||
|
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_NX] = V->n[0]*normFlip;
|
||||||
|
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_NY] = V->n[1]*normFlip;
|
||||||
|
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_NZ] = V->n[2]*normFlip;
|
||||||
|
|
||||||
|
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_U] = V->u;
|
||||||
|
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_V] = V->v;
|
||||||
|
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXTURE_X] = texBaseX;
|
||||||
|
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXTURE_Y] = texBaseY;
|
||||||
|
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXTURE_W] = texWidth;
|
||||||
|
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXTURE_H] = texHeight;
|
||||||
|
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXPARAMS_EN] = texEnable ? 1.0f : 0.0f;
|
||||||
|
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXPARAMS_TRANS] = contourProcessing;
|
||||||
|
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXPARAMS_UWRAP] = (P->header[2]&2) ? 1.0f : 0.0f;
|
||||||
|
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXPARAMS_VWRAP] = (P->header[2]&1) ? 1.0f : 0.0f;
|
||||||
|
Cache->verts[s][baseIdx + VBO_VERTEX_OFFSET_TEXFORMAT_CONTOUR] = (texFormat==0) ? 1.0f : 0.0f;
|
||||||
|
|
||||||
|
Cache->curVertIdx[s]++;
|
||||||
|
Cache->vboCurOffset += VBO_VERTEX_SIZE*sizeof(GLfloat);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL CRender3D::InsertPolygon(ModelCache *Cache, const Poly *P)
|
||||||
|
{
|
||||||
|
GLfloat n[3], v1[3], v2[3], normZFlip;
|
||||||
|
int i;
|
||||||
|
BOOL doubleSided;
|
||||||
|
|
||||||
|
// Bounds testing: up to 12 triangles will be inserted (worst case: double sided quad is 6 triangles)
|
||||||
|
if ((Cache->curVertIdx[P->state]+6*2) >= Cache->maxVertIdx)
|
||||||
|
return ErrorLocalVertexOverflow(); // local buffers are not expected to overflow
|
||||||
|
if ((Cache->vboCurOffset+6*2*VBO_VERTEX_SIZE*sizeof(GLfloat)) >= Cache->vboMaxOffset)
|
||||||
|
return FAIL; // this just indicates we may need to re-cache
|
||||||
|
|
||||||
|
// Is the polygon double sided?
|
||||||
|
doubleSided = (P->header[1]&0x10) ? TRUE : FALSE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determine polygon winding by taking cross product of vectors formed from
|
||||||
|
* 3 polygon vertices (the middle one being the origin). In reality, back-
|
||||||
|
* face culling is determined by the polygon normal and two-sided polygons
|
||||||
|
* exist. This is just a temporary hack.
|
||||||
|
*
|
||||||
|
* If the cross product points the same way as the normal, the winding is
|
||||||
|
* clockwise and can be kept, otherwise it must be reversed.
|
||||||
|
*
|
||||||
|
* NOTE: This assumes that the Model 3 base coordinate system's Z axis
|
||||||
|
* (into the screen) -1, like OpenGL's. For some games (eg., Lost World),
|
||||||
|
* this is not the case. Assuming games consistently use the same type of
|
||||||
|
* coordinate system matrix, it seems that inverting the whole dot product
|
||||||
|
* when Z is positive helps. I don't understand why...
|
||||||
|
*/
|
||||||
|
v1[0] = P->Vert[0].x-P->Vert[1].x;
|
||||||
|
v1[1] = P->Vert[0].y-P->Vert[1].y;
|
||||||
|
v1[2] = P->Vert[0].z-P->Vert[1].z;
|
||||||
|
v2[0] = P->Vert[2].x-P->Vert[1].x;
|
||||||
|
v2[1] = P->Vert[2].y-P->Vert[1].y;
|
||||||
|
v2[2] = P->Vert[2].z-P->Vert[1].z;
|
||||||
|
CrossProd(n,v1,v2);
|
||||||
|
|
||||||
|
normZFlip = -1.0f*matrixBasePtr[0x5]; // coordinate system m13 component
|
||||||
|
|
||||||
|
if (normZFlip*(n[0]*P->n[0]+n[1]*P->n[1]+n[2]*P->n[2]) >= 0.0) // clockwise winding confirmed
|
||||||
|
{
|
||||||
|
// Store the first triangle
|
||||||
|
for (i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
InsertVertex(Cache, &(P->Vert[i]), P, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doubleSided) // store backside as counter-clockwise
|
||||||
|
{
|
||||||
|
for (i = 2; i >=0; i--)
|
||||||
|
{
|
||||||
|
InsertVertex(Cache, &(P->Vert[i]), P, -1.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If quad, second triangle will just be vertices 1, 3, 4
|
||||||
|
if (P->numVerts == 4)
|
||||||
|
{
|
||||||
|
InsertVertex(Cache, &(P->Vert[0]), P, 1.0f);
|
||||||
|
InsertVertex(Cache, &(P->Vert[2]), P, 1.0f);
|
||||||
|
InsertVertex(Cache, &(P->Vert[3]), P, 1.0f);
|
||||||
|
|
||||||
|
if (doubleSided)
|
||||||
|
{
|
||||||
|
InsertVertex(Cache, &(P->Vert[0]), P, -1.0f);
|
||||||
|
InsertVertex(Cache, &(P->Vert[3]), P, -1.0f);
|
||||||
|
InsertVertex(Cache, &(P->Vert[2]), P, -1.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // counterclockwise winding, reverse it
|
||||||
|
{
|
||||||
|
for (i = 2; i >=0; i--)
|
||||||
|
{
|
||||||
|
InsertVertex(Cache, &(P->Vert[i]), P, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doubleSided) // store backside as clockwise
|
||||||
|
{
|
||||||
|
for (i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
InsertVertex(Cache, &(P->Vert[i]), P, -1.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (P->numVerts == 4)
|
||||||
|
{
|
||||||
|
InsertVertex(Cache, &(P->Vert[0]), P, 1.0f);
|
||||||
|
InsertVertex(Cache, &(P->Vert[3]), P, 1.0f);
|
||||||
|
InsertVertex(Cache, &(P->Vert[2]), P, 1.0f);
|
||||||
|
|
||||||
|
if (doubleSided)
|
||||||
|
{
|
||||||
|
InsertVertex(Cache, &(P->Vert[0]), P, -1.0f);
|
||||||
|
InsertVertex(Cache, &(P->Vert[2]), P, -1.0f);
|
||||||
|
InsertVertex(Cache, &(P->Vert[3]), P, -1.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return OKAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Begins caching a new model by resetting to the start of the local vertex buffer
|
||||||
|
BOOL CRender3D::BeginModel(ModelCache *Cache)
|
||||||
|
{
|
||||||
|
int m;
|
||||||
|
|
||||||
|
// Determine whether we've exceeded the model cache limits (caller will have to recache)
|
||||||
|
if (Cache->numModels >= Cache->maxModels)
|
||||||
|
return FAIL; // too many models
|
||||||
|
//return ErrorLog("Too many %s models.", Cache->dynamic?"dynamic":"static");
|
||||||
|
|
||||||
|
m = Cache->numModels;
|
||||||
|
|
||||||
|
// Reset to the beginning of the local vertex buffer
|
||||||
|
for (int i = 0; i < 2; i++)
|
||||||
|
Cache->curVertIdx[i] = 0;
|
||||||
|
|
||||||
|
// Clear the VBO reference to 0
|
||||||
|
memset(&(Cache->Models[m]), 0, sizeof(VBORef));
|
||||||
|
|
||||||
|
// Record starting index of first opaque polygon in VBO (alpha poly index will be re-set in EndModel())
|
||||||
|
Cache->Models[m].index[POLY_STATE_NORMAL] = Cache->vboCurOffset/(VBO_VERTEX_SIZE*sizeof(GLfloat));
|
||||||
|
Cache->Models[m].index[POLY_STATE_ALPHA] = Cache->Models[m].index[POLY_STATE_NORMAL];
|
||||||
|
|
||||||
|
return OKAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uploads all vertices from the local vertex buffer to the VBO, sets up the VBO reference, updates the LUT
|
||||||
|
void CRender3D::EndModel(ModelCache *Cache, int lutIdx)
|
||||||
|
{
|
||||||
|
int m;
|
||||||
|
|
||||||
|
m = Cache->numModels++;
|
||||||
|
|
||||||
|
// Record the number of vertices, completing the VBORef
|
||||||
|
for (int i = 0; i < 2; i++)
|
||||||
|
Cache->Models[m].numVerts[i] = Cache->curVertIdx[i];
|
||||||
|
|
||||||
|
// First alpha polygon immediately follows the normal polygons
|
||||||
|
Cache->Models[m].index[POLY_STATE_ALPHA] = Cache->Models[m].index[POLY_STATE_NORMAL]+Cache->Models[m].numVerts[POLY_STATE_NORMAL];
|
||||||
|
|
||||||
|
// Upload from local vertex buffer to real VBO
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, Cache->vboID);
|
||||||
|
if (Cache->Models[m].numVerts[POLY_STATE_NORMAL] > 0)
|
||||||
|
glBufferSubData(GL_ARRAY_BUFFER, Cache->Models[m].index[POLY_STATE_NORMAL]*VBO_VERTEX_SIZE*sizeof(GLfloat), Cache->curVertIdx[POLY_STATE_NORMAL]*VBO_VERTEX_SIZE*sizeof(GLfloat), Cache->verts[POLY_STATE_NORMAL]);
|
||||||
|
if (Cache->Models[m].numVerts[POLY_STATE_ALPHA] > 0)
|
||||||
|
glBufferSubData(GL_ARRAY_BUFFER, Cache->Models[m].index[POLY_STATE_ALPHA]*VBO_VERTEX_SIZE*sizeof(GLfloat), Cache->curVertIdx[POLY_STATE_ALPHA]*VBO_VERTEX_SIZE*sizeof(GLfloat), Cache->verts[POLY_STATE_ALPHA]);
|
||||||
|
|
||||||
|
// Update the LUT
|
||||||
|
Cache->lut[lutIdx] = m;
|
||||||
|
|
||||||
|
// Record LUT index in the model VBORef
|
||||||
|
Cache->Models[m].lutIdx = lutIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CacheModel():
|
||||||
|
*
|
||||||
|
* Decodes and caches a complete model. Returns FAIL if any sort of overflow in
|
||||||
|
* the cache occurred. In this case, the model cache should be cleared before
|
||||||
|
* being used again because an incomplete model will be stored, wasting vertex
|
||||||
|
* buffer space.
|
||||||
|
*/
|
||||||
|
|
||||||
|
BOOL CRender3D::CacheModel(ModelCache *Cache, int lutIdx, const UINT32 *data)
|
||||||
|
{
|
||||||
|
Vertex Prev[4]; // previous vertices
|
||||||
|
int numPolys = 0;
|
||||||
|
BOOL done = FALSE;
|
||||||
|
|
||||||
|
// Sega Rally 2 bad models
|
||||||
|
//if (lutIdx == 0x27a1 || lutIdx == 0x21e0)
|
||||||
|
// return FAIL;
|
||||||
|
|
||||||
|
if (data == NULL)
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
// Start constructing a new model
|
||||||
|
if (FAIL == BeginModel(Cache))
|
||||||
|
return FAIL; // too many models!
|
||||||
|
|
||||||
|
// Cache all polygons
|
||||||
|
while (!done)
|
||||||
|
{
|
||||||
|
Poly P; // current polygon
|
||||||
|
GLfloat mag;
|
||||||
|
GLfloat uvScale;
|
||||||
|
int texFormat, texWidth, texHeight, texPage, texBaseX, texBaseY;
|
||||||
|
unsigned i, j, vmask;
|
||||||
|
UINT32 ix, iy, iz, it;
|
||||||
|
|
||||||
|
// Set current header pointer (header is 7 words)
|
||||||
|
P.header = data;
|
||||||
|
data += 7; // data will now point to first vertex
|
||||||
|
if (P.header[6]==0)// || P.header[0]==0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Obtain basic polygon parameters
|
||||||
|
done = P.header[1]&4; // last polygon?
|
||||||
|
P.numVerts = (P.header[0]&0x40)?4:3;
|
||||||
|
|
||||||
|
// Texture data
|
||||||
|
texFormat = (P.header[6]>>7)&7;
|
||||||
|
texWidth = (32<<((P.header[3]>>3)&7));
|
||||||
|
texHeight = (32<<((P.header[3]>>0)&7));
|
||||||
|
texPage = (P.header[4]&0x40) ? 1024 : 0; // treat texture page as Y coordinate
|
||||||
|
texBaseX = (32*(((P.header[4]&0x1F)<<1)|((P.header[5]>>7)&1)));
|
||||||
|
texBaseY = (32*(P.header[5]&0x1F)+texPage);
|
||||||
|
uvScale = (P.header[1]&0x40)?1.0f:(1.0f/8.0f);
|
||||||
|
|
||||||
|
// Determine whether this is an alpha polygon
|
||||||
|
if (((P.header[6]&0x00800000)==0) || // translucent polygon
|
||||||
|
(texFormat==7) || // RGBA4 texture
|
||||||
|
(texFormat==4)) // A4L4 texture
|
||||||
|
P.state = POLY_STATE_ALPHA;
|
||||||
|
else
|
||||||
|
P.state = POLY_STATE_NORMAL;
|
||||||
|
if (texFormat==1) // A4L4 interleaved
|
||||||
|
{
|
||||||
|
if ((P.header[6]&2))
|
||||||
|
P.state = POLY_STATE_ALPHA;
|
||||||
|
else
|
||||||
|
P.state = POLY_STATE_NORMAL;
|
||||||
|
}
|
||||||
|
if (texFormat==3) // A4L4 interleaved
|
||||||
|
{
|
||||||
|
if ((P.header[6]&4))
|
||||||
|
P.state = POLY_STATE_ALPHA;
|
||||||
|
else
|
||||||
|
P.state = POLY_STATE_NORMAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode the texture
|
||||||
|
DecodeTexture(texFormat, texBaseX, texBaseY, texWidth, texHeight);
|
||||||
|
|
||||||
|
// Polygon normal is in upper 24 bits: sign + 1.22 fixed point
|
||||||
|
P.n[0] = (GLfloat) (((INT32)P.header[1])>>8) * (1.0f/4194304.0f);
|
||||||
|
P.n[1] = (GLfloat) (((INT32)P.header[2])>>8) * (1.0f/4194304.0f);
|
||||||
|
P.n[2] = (GLfloat) (((INT32)P.header[3])>>8) * (1.0f/4194304.0f);
|
||||||
|
|
||||||
|
// Fetch reused vertices according to bitfield, then new verts
|
||||||
|
i = 0;
|
||||||
|
j = 0;
|
||||||
|
vmask = 1;
|
||||||
|
for (i = 0; i < 4; i++) // up to 4 reused vertices
|
||||||
|
{
|
||||||
|
if ((P.header[0x00]&vmask))
|
||||||
|
{
|
||||||
|
P.Vert[j] = Prev[i];
|
||||||
|
++j;
|
||||||
|
}
|
||||||
|
vmask <<= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; j < P.numVerts; j++) // remaining vertices are new and defined here
|
||||||
|
{
|
||||||
|
// Fetch vertices
|
||||||
|
ix = data[0];
|
||||||
|
iy = data[1];
|
||||||
|
iz = data[2];
|
||||||
|
it = data[3];
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Check for bad vertices (Sega Rally 2)
|
||||||
|
if (((ix>>28)==7) || ((iy>>28)==7) || ((iz>>28)==7))
|
||||||
|
{
|
||||||
|
//printf("%X ix=%08X, iy=%08X, iz=%08X\n", lutIdx, ix, iy, iz);
|
||||||
|
goto StopDecoding;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Decode vertices
|
||||||
|
P.Vert[j].x = (GLfloat) (((INT32)ix)>>8) * vertexFactor;
|
||||||
|
P.Vert[j].y = (GLfloat) (((INT32)iy)>>8) * vertexFactor;
|
||||||
|
P.Vert[j].z = (GLfloat) (((INT32)iz)>>8) * vertexFactor;
|
||||||
|
P.Vert[j].n[0] = P.n[0]+(GLfloat)(INT8)(ix&0xFF); // vertex normals are offset from polygon normal
|
||||||
|
P.Vert[j].n[1] = P.n[1]+(GLfloat)(INT8)(iy&0xFF);
|
||||||
|
P.Vert[j].n[2] = P.n[2]+(GLfloat)(INT8)(iz&0xFF);
|
||||||
|
P.Vert[j].u = (GLfloat) ((UINT16)(it>>16)) * uvScale; // TO-DO: might these be signed?
|
||||||
|
P.Vert[j].v = (GLfloat) ((UINT16)(it&0xFFFF)) * uvScale;
|
||||||
|
data += 4;
|
||||||
|
|
||||||
|
// Normalize the vertex normal
|
||||||
|
mag = sqrt(P.Vert[j].n[0]*P.Vert[j].n[0]+P.Vert[j].n[1]*P.Vert[j].n[1]+P.Vert[j].n[2]*P.Vert[j].n[2]);
|
||||||
|
P.Vert[j].n[0] /= mag;
|
||||||
|
P.Vert[j].n[1] /= mag;
|
||||||
|
P.Vert[j].n[2] /= mag;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy current vertices into previous vertex array
|
||||||
|
for (i = 0; i < 4; i++)
|
||||||
|
Prev[i] = P.Vert[i];
|
||||||
|
|
||||||
|
// Copy this polygon into the model buffer
|
||||||
|
if (OKAY != InsertPolygon(Cache,&P))
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
++numPolys;
|
||||||
|
}
|
||||||
|
StopDecoding:
|
||||||
|
|
||||||
|
// Finish model and enter it into the LUT
|
||||||
|
EndModel(Cache,lutIdx);
|
||||||
|
|
||||||
|
return OKAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Cache Management
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
// Use this to determine if a model needs to be cached (returns TRUE if so)
|
||||||
|
BOOL CRender3D::NeedToCache(ModelCache *Cache, int lutIdx)
|
||||||
|
{
|
||||||
|
//if (Cache->dynamic) // never permanently store models in dynamic caches
|
||||||
|
// return TRUE;
|
||||||
|
return (Cache->lut[lutIdx]<0) ? TRUE : FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discard all models in the cache and the display list
|
||||||
|
void CRender3D::ClearModelCache(ModelCache *Cache)
|
||||||
|
{
|
||||||
|
Cache->vboCurOffset = 0;
|
||||||
|
for (int i = 0; i < 2; i++)
|
||||||
|
Cache->curVertIdx[i] = 0;
|
||||||
|
if (!Cache->dynamic)
|
||||||
|
memset(Cache->lut, 0xFF, sizeof(INT16)*Cache->lutSize); // set all to -1
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Cache->numModels; i++)
|
||||||
|
Cache->lut[Cache->Models[i].lutIdx] = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Cache->numModels = 0;
|
||||||
|
ClearDisplayList(Cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL CRender3D::CreateModelCache(ModelCache *Cache, unsigned vboMaxVerts,
|
||||||
|
unsigned localMaxVerts, unsigned maxNumModels, unsigned numLUTEntries,
|
||||||
|
unsigned displayListSize, BOOL isDynamic)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
int vboBytes, localBytes;
|
||||||
|
BOOL success;
|
||||||
|
|
||||||
|
Cache->dynamic = isDynamic;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* VBO allocation:
|
||||||
|
*
|
||||||
|
* Progressively smaller VBOs, in steps of localMaxVerts are allocated
|
||||||
|
* until successful. If the size dips below localMaxVerts, localMaxVerts is
|
||||||
|
* attempted as the final try.
|
||||||
|
*/
|
||||||
|
|
||||||
|
glGetError(); // clear error flag
|
||||||
|
glGenBuffers(1, &(Cache->vboID));
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, Cache->vboID);
|
||||||
|
|
||||||
|
vboBytes = vboMaxVerts*VBO_VERTEX_SIZE*sizeof(GLfloat);
|
||||||
|
localBytes = localMaxVerts*VBO_VERTEX_SIZE*sizeof(GLfloat);
|
||||||
|
|
||||||
|
// Try allocating until size is
|
||||||
|
success = FALSE;
|
||||||
|
while (vboBytes >= localBytes)
|
||||||
|
{
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, vboBytes, 0, isDynamic?GL_STREAM_DRAW:GL_STATIC_DRAW);
|
||||||
|
if (glGetError() == GL_NO_ERROR)
|
||||||
|
{
|
||||||
|
success = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
vboBytes -= localBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
// Last ditch attempt: try the local buffer size
|
||||||
|
vboBytes = localBytes;
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, vboBytes, 0, isDynamic?GL_STREAM_DRAW:GL_STATIC_DRAW);
|
||||||
|
if (glGetError() != GL_NO_ERROR)
|
||||||
|
return ErrorLog("OpenGL was unable to provide a %s vertex buffer.", isDynamic?"dynamic":"static");
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugLog("%s vertex buffer size: %1.2f MB", isDynamic?"Dynamic":"Static", (float)vboBytes/(float)0x100000);
|
||||||
|
InfoLog("%s vertex buffer size: %1.2f MB", isDynamic?"Dynamic":"Static", (float)vboBytes/(float)0x100000);
|
||||||
|
|
||||||
|
// Set the VBO to the size we obtained
|
||||||
|
Cache->vboMaxOffset = vboBytes;
|
||||||
|
Cache->vboCurOffset = 0;
|
||||||
|
|
||||||
|
// Attempt to allocate space for local VBO
|
||||||
|
for (i = 0; i < 2; i++)
|
||||||
|
{
|
||||||
|
Cache->verts[i] = new(std::nothrow) GLfloat[localMaxVerts*VBO_VERTEX_SIZE];
|
||||||
|
Cache->curVertIdx[i] = 0;
|
||||||
|
}
|
||||||
|
Cache->maxVertIdx = localMaxVerts;
|
||||||
|
|
||||||
|
// ... model array
|
||||||
|
Cache->Models = new(std::nothrow) VBORef[maxNumModels];
|
||||||
|
Cache->maxModels = maxNumModels;
|
||||||
|
Cache->numModels = 0;
|
||||||
|
|
||||||
|
// ... LUT
|
||||||
|
Cache->lut = new(std::nothrow) INT16[numLUTEntries];
|
||||||
|
Cache->lutSize = numLUTEntries;
|
||||||
|
|
||||||
|
// ... display list
|
||||||
|
Cache->List = new(std::nothrow) DisplayList[displayListSize];
|
||||||
|
ClearDisplayList(Cache);
|
||||||
|
Cache->maxListSize = displayListSize;
|
||||||
|
|
||||||
|
// Check if memory allocation succeeded
|
||||||
|
if ((Cache->verts[0]==NULL) || (Cache->verts[1]==NULL) || (Cache->Models==NULL) || (Cache->lut==NULL) || (Cache->List==NULL))
|
||||||
|
{
|
||||||
|
DestroyModelCache(Cache);
|
||||||
|
return ErrorLog("Insufficient memory for model cache.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear LUT (MUST be done here because ClearModelCache() won't do it for dynamic models)
|
||||||
|
for (i = 0; i < numLUTEntries; i++)
|
||||||
|
Cache->lut[i] = -1;
|
||||||
|
|
||||||
|
// All good!
|
||||||
|
return OKAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CRender3D::DestroyModelCache(ModelCache *Cache)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 2; i++)
|
||||||
|
{
|
||||||
|
if (Cache->verts[i] != NULL)
|
||||||
|
delete [] Cache->verts[i];
|
||||||
|
}
|
||||||
|
if (Cache->Models != NULL)
|
||||||
|
delete [] Cache->Models;
|
||||||
|
if (Cache->lut != NULL)
|
||||||
|
delete [] Cache->lut;
|
||||||
|
if (Cache->List != NULL)
|
||||||
|
delete [] Cache->List;
|
||||||
|
|
||||||
|
memset(Cache, 0, sizeof(ModelCache));
|
||||||
|
}
|
170
Graphics/Render.h
Normal file
170
Graphics/Render.h
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Render.h
|
||||||
|
*
|
||||||
|
* Header file defining the CRender class: container for both the 2D and 3D
|
||||||
|
* renderers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDED_RENDER_H
|
||||||
|
#define INCLUDED_RENDER_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CRender:
|
||||||
|
*
|
||||||
|
* Tile generator graphics engine. This must be constructed and initialized
|
||||||
|
* before being attached to any objects that want to make use of it. Apart from
|
||||||
|
* the constructor, all members assume that a global GL device
|
||||||
|
* context is available and that GL functions may be called.
|
||||||
|
*/
|
||||||
|
class CRender2D
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
* BeginFrame(layerFormats):
|
||||||
|
*
|
||||||
|
* Prepare to render a new frame. Must be called once per frame prior to
|
||||||
|
* drawing anything.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* layerFormats A bit field indicating how to interpret each of the
|
||||||
|
* four layers: 0=8-bit pixels, 1=4-bit pixels. Bits
|
||||||
|
* 3-0 correspond to layers 3-0.
|
||||||
|
*/
|
||||||
|
void BeginFrame(unsigned layerFormats);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* EndFrame(void):
|
||||||
|
*
|
||||||
|
* Signals the end of rendering for this frame. Must be called last during
|
||||||
|
* the frame.
|
||||||
|
*/
|
||||||
|
void EndFrame(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WriteVRAM(addr, data):
|
||||||
|
*
|
||||||
|
* Indicates what will be written next to the tile generator's RAM. The
|
||||||
|
* VRAM address must not have yet been updated, to allow the renderer to
|
||||||
|
* check for changes. Data is accepted in the same form as the tile
|
||||||
|
* generator: the MSB is what was written to addr+3. This function is
|
||||||
|
* intended to facilitate on-the-fly decoding of tiles and palette data.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* addr Address in tile generator RAM. Caller must ensure it is
|
||||||
|
* clamped to the range 0x000000 to 0x11FFFF because this
|
||||||
|
* function does not.
|
||||||
|
* data The data to write.
|
||||||
|
*/
|
||||||
|
void WriteVRAM(unsigned addr, UINT32 data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AttachRegisters(regPtr):
|
||||||
|
*
|
||||||
|
* Attaches tile generator registers. This must be done prior to any
|
||||||
|
* rendering otherwise the program may crash with an access violation.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* regPtr Pointer to the base of the tile generator registers.
|
||||||
|
* There are assumed to be 64 in all.
|
||||||
|
*/
|
||||||
|
void AttachRegisters(const UINT32 *regPtr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AttachVRAM(vramPtr):
|
||||||
|
*
|
||||||
|
* Attaches tile generator RAM. This must be done prior to any rendering
|
||||||
|
* otherwise the program may crash with an access violation.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* vramPtr Pointer to the base of the tile generator RAM (0x120000
|
||||||
|
* bytes). VRAM is assumed to be in little endian format.
|
||||||
|
*/
|
||||||
|
void AttachVRAM(const UINT8 *vramPtr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Init(xOffset, yOffset, xRes, yRes):
|
||||||
|
*
|
||||||
|
* One-time initialization of the context. Must be called before any other
|
||||||
|
* members (meaning it should be called even before being attached to any
|
||||||
|
* other objects that want to use it).
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* xOffset X offset within OpenGL display surface in pixels.
|
||||||
|
* yOffset Y offset.
|
||||||
|
* xRes Horizontal resolution of display surface in pixels.
|
||||||
|
* yRes Vertical resolution.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* OKAY is successful, otherwise FAILED if a non-recoverable error
|
||||||
|
* occurred. Prints own error messages.
|
||||||
|
*/
|
||||||
|
BOOL Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CRender2D(void):
|
||||||
|
* ~CRender2D(void):
|
||||||
|
*
|
||||||
|
* Constructor and destructor.
|
||||||
|
*/
|
||||||
|
CRender2D(void);
|
||||||
|
~CRender2D(void);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Private member functions
|
||||||
|
void DrawTile4Bit(unsigned tile, UINT32 *buf, unsigned pitch);
|
||||||
|
void DrawTile8Bit(unsigned tile, UINT32 *buf, unsigned pitch);
|
||||||
|
void DrawRect(int layerNum, UINT32 *layerSurf, const UINT32 *nameTable, unsigned x, unsigned y, unsigned w, unsigned h);
|
||||||
|
void UpdateLayer(int layerNum);
|
||||||
|
void DisplayLayer(int layerNum, GLfloat z, GLfloat scroll);
|
||||||
|
void Setup2D(void);
|
||||||
|
|
||||||
|
// Data received from tile generator device object
|
||||||
|
const UINT32 *vram;
|
||||||
|
const UINT32 *regs;
|
||||||
|
|
||||||
|
// OpenGL data
|
||||||
|
GLuint texID[4]; // IDs for the 4 layer textures
|
||||||
|
unsigned xPixels, yPixels; // display surface resolution
|
||||||
|
unsigned xOffs, yOffs; // offset
|
||||||
|
|
||||||
|
// Shader programs and input data locations
|
||||||
|
GLuint shaderProgram; // shader program object
|
||||||
|
GLuint vertexShader; // vertex shader handle
|
||||||
|
GLuint fragmentShader; // fragment shader
|
||||||
|
GLuint textureMapLoc; // location of "textureMap" uniform
|
||||||
|
GLuint bottomLayerLoc; // uniform
|
||||||
|
|
||||||
|
|
||||||
|
// Dirty rectangles (non-zero indicates region is dirty)
|
||||||
|
UINT8 dirty[4][64/DIRTY_RECT_HEIGHT][64/DIRTY_RECT_WIDTH];
|
||||||
|
BOOL allDirty; // global dirty flag (forces everything to be updated)
|
||||||
|
|
||||||
|
// Buffers
|
||||||
|
UINT8 *memoryPool; // all memory is allocated here
|
||||||
|
UINT32 *surf; // 4 512x512x32bpp pixel surfaces
|
||||||
|
UINT32 *pal; // 0x20000 byte (32K colors) palette
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // INCLUDED_RENDER2D_H
|
871
Graphics/Render2D.cpp
Normal file
871
Graphics/Render2D.cpp
Normal file
|
@ -0,0 +1,871 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Render2D.cpp
|
||||||
|
*
|
||||||
|
* Implementation of the CRender2D class: OpenGL tile generator graphics.
|
||||||
|
*
|
||||||
|
* To-Do List
|
||||||
|
* ----------
|
||||||
|
* - Add dirty rectangles? There is already some inactive code in here for
|
||||||
|
* this purpose and it needs to be updated or deleted once and for all.
|
||||||
|
* - Are v-scroll values 9 or 10 bits?
|
||||||
|
* - Add fast paths for no scrolling (including unclipped tile rendering).
|
||||||
|
* - Inline the loops in the tile renderers.
|
||||||
|
* - Update description of tile generator before you forget :)
|
||||||
|
* - A proper shut-down function is needed! OpenGL might not be available when
|
||||||
|
* the destructor for this class is called.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "Pkgs/glew.h"
|
||||||
|
#include "Supermodel.h"
|
||||||
|
#include "Graphics/Shaders2D.h" // fragment and vertex shaders
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Definitions and Constants
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
// Shader program files (for use in development builds only)
|
||||||
|
#define VERTEX_2D_SHADER_FILE "Src/Graphics/Vertex2D.glsl"
|
||||||
|
#define FRAGMENT_2D_SHADER_FILE "Src/Graphics/Fragment2D.glsl"
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Tile Drawing Functions
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
// Draw 4-bit tile line, no clipping performed
|
||||||
|
void CRender2D::DrawTileLine4BitNoClip(UINT32 *buf, UINT16 tile, int tileLine)
|
||||||
|
{
|
||||||
|
unsigned tileOffset; // offset of tile pattern within VRAM
|
||||||
|
unsigned palette; // color palette bits obtained from tile
|
||||||
|
UINT32 pattern; // 8 pattern pixels fetched at once
|
||||||
|
|
||||||
|
// Tile pattern offset: each tile occupies 32 bytes when using 4-bit pixels
|
||||||
|
tileOffset = ((tile&0x3FFF)<<1) | ((tile>>15)&1);
|
||||||
|
tileOffset *= 32;
|
||||||
|
tileOffset /= 4; // VRAM is a UINT32 array
|
||||||
|
|
||||||
|
// Upper color bits; the lower 4 bits come from the tile pattern
|
||||||
|
palette = tile&0x7FF0;
|
||||||
|
|
||||||
|
// Draw 8 pixels
|
||||||
|
pattern = vram[tileOffset+tileLine];
|
||||||
|
*buf++ = pal[((pattern>>28)&0xF) | palette];
|
||||||
|
*buf++ = pal[((pattern>>24)&0xF) | palette];
|
||||||
|
*buf++ = pal[((pattern>>20)&0xF) | palette];
|
||||||
|
*buf++ = pal[((pattern>>16)&0xF) | palette];
|
||||||
|
*buf++ = pal[((pattern>>12)&0xF) | palette];
|
||||||
|
*buf++ = pal[((pattern>>8)&0xF) | palette];
|
||||||
|
*buf++ = pal[((pattern>>4)&0xF) | palette];
|
||||||
|
*buf++ = pal[((pattern>>0)&0xF) | palette];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw 8-bit tile line, clipped at left edge
|
||||||
|
void CRender2D::DrawTileLine8BitNoClip(UINT32 *buf, UINT16 tile, int tileLine)
|
||||||
|
{
|
||||||
|
unsigned tileOffset; // offset of tile pattern within VRAM
|
||||||
|
unsigned palette; // color palette bits obtained from tile
|
||||||
|
UINT32 pattern; // 4 pattern pixels fetched at once
|
||||||
|
|
||||||
|
tileLine *= 2; // 8-bit pixels, each line is two words
|
||||||
|
|
||||||
|
// Tile pattern offset: each tile occupies 64 bytes when using 8-bit pixels
|
||||||
|
tileOffset = tile&0x3FFF;
|
||||||
|
tileOffset *= 64;
|
||||||
|
tileOffset /= 4;
|
||||||
|
|
||||||
|
// Upper color bits
|
||||||
|
palette = tile&0x7F00;
|
||||||
|
|
||||||
|
// Draw 4 pixels at a time
|
||||||
|
pattern = vram[tileOffset+tileLine];
|
||||||
|
*buf++ = pal[((pattern>>24)&0xFF) | palette];
|
||||||
|
*buf++ = pal[((pattern>>16)&0xFF) | palette];
|
||||||
|
*buf++ = pal[((pattern>>8)&0xFF) | palette];
|
||||||
|
*buf++ = pal[((pattern>>0)&0xFF) | palette];
|
||||||
|
pattern = vram[tileOffset+tileLine+1];
|
||||||
|
*buf++ = pal[((pattern>>24)&0xFF) | palette];
|
||||||
|
*buf++ = pal[((pattern>>16)&0xFF) | palette];
|
||||||
|
*buf++ = pal[((pattern>>8)&0xFF) | palette];
|
||||||
|
*buf++ = pal[((pattern>>0)&0xFF) | palette];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Draw 4-bit tile line, clipped at left edge
|
||||||
|
void CRender2D::DrawTileLine4Bit(UINT32 *buf, int offset, UINT16 tile, int tileLine)
|
||||||
|
{
|
||||||
|
unsigned tileOffset; // offset of tile pattern within VRAM
|
||||||
|
unsigned palette; // color palette bits obtained from tile
|
||||||
|
UINT32 pattern; // 8 pattern pixels fetched at once
|
||||||
|
|
||||||
|
// Tile pattern offset: each tile occupies 32 bytes when using 4-bit pixels
|
||||||
|
tileOffset = ((tile&0x3FFF)<<1) | ((tile>>15)&1);
|
||||||
|
tileOffset *= 32;
|
||||||
|
tileOffset /= 4; // VRAM is a UINT32 array
|
||||||
|
|
||||||
|
// Upper color bits; the lower 4 bits come from the tile pattern
|
||||||
|
palette = tile&0x7FF0;
|
||||||
|
|
||||||
|
// Draw 8 pixels
|
||||||
|
pattern = vram[tileOffset+tileLine];
|
||||||
|
for (int bitPos = 28; bitPos >= 0; bitPos -= 4)
|
||||||
|
{
|
||||||
|
if (offset >= 0)
|
||||||
|
buf[offset] = pal[((pattern>>bitPos)&0xF) | palette];
|
||||||
|
++offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw 4-bit tile line, clipped at right edge
|
||||||
|
void CRender2D::DrawTileLine4BitRightClip(UINT32 *buf, int offset, UINT16 tile, int tileLine, int numPixels)
|
||||||
|
{
|
||||||
|
unsigned tileOffset; // offset of tile pattern within VRAM
|
||||||
|
unsigned palette; // color palette bits obtained from tile
|
||||||
|
UINT32 pattern; // 8 pattern pixels fetched at once
|
||||||
|
int bitPos;
|
||||||
|
|
||||||
|
// Tile pattern offset: each tile occupies 32 bytes when using 4-bit pixels
|
||||||
|
tileOffset = ((tile&0x3FFF)<<1) | ((tile>>15)&1);
|
||||||
|
tileOffset *= 32;
|
||||||
|
tileOffset /= 4; // VRAM is a UINT32 array
|
||||||
|
|
||||||
|
// Upper color bits; the lower 4 bits come from the tile pattern
|
||||||
|
palette = tile&0x7FF0;
|
||||||
|
|
||||||
|
// Draw 8 pixels
|
||||||
|
pattern = vram[tileOffset+tileLine];
|
||||||
|
bitPos = 28;
|
||||||
|
for (int i = 0; i < numPixels; i++)
|
||||||
|
{
|
||||||
|
buf[offset] = pal[((pattern>>bitPos)&0xF) | palette];
|
||||||
|
++offset;
|
||||||
|
bitPos -= 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw 8-bit tile line, clipped at left edge
|
||||||
|
void CRender2D::DrawTileLine8Bit(UINT32 *buf, int offset, UINT16 tile, int tileLine)
|
||||||
|
{
|
||||||
|
unsigned tileOffset; // offset of tile pattern within VRAM
|
||||||
|
unsigned palette; // color palette bits obtained from tile
|
||||||
|
UINT32 pattern; // 4 pattern pixels fetched at once
|
||||||
|
|
||||||
|
tileLine *= 2; // 8-bit pixels, each line is two words
|
||||||
|
|
||||||
|
// Tile pattern offset: each tile occupies 64 bytes when using 8-bit pixels
|
||||||
|
tileOffset = tile&0x3FFF;
|
||||||
|
tileOffset *= 64;
|
||||||
|
tileOffset /= 4;
|
||||||
|
|
||||||
|
// Upper color bits
|
||||||
|
palette = tile&0x7F00;
|
||||||
|
|
||||||
|
// Draw 4 pixels at a time
|
||||||
|
pattern = vram[tileOffset+tileLine];
|
||||||
|
for (int bitPos = 24; bitPos >= 0; bitPos -= 8)
|
||||||
|
{
|
||||||
|
if (offset >= 0)
|
||||||
|
buf[offset] = pal[((pattern>>bitPos)&0xFF) | palette];
|
||||||
|
++offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
pattern = vram[tileOffset+tileLine+1];
|
||||||
|
for (int bitPos = 24; bitPos >= 0; bitPos -= 8)
|
||||||
|
{
|
||||||
|
if (offset >= 0)
|
||||||
|
buf[offset] = pal[((pattern>>bitPos)&0xFF) | palette];
|
||||||
|
++offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw 8-bit tile line, clipped at right edge
|
||||||
|
void CRender2D::DrawTileLine8BitRightClip(UINT32 *buf, int offset, UINT16 tile, int tileLine, int numPixels)
|
||||||
|
{
|
||||||
|
unsigned tileOffset; // offset of tile pattern within VRAM
|
||||||
|
unsigned palette; // color palette bits obtained from tile
|
||||||
|
UINT32 pattern; // 4 pattern pixels fetched at once
|
||||||
|
int bitPos;
|
||||||
|
|
||||||
|
tileLine *= 2; // 8-bit pixels, each line is two words
|
||||||
|
|
||||||
|
// Tile pattern offset: each tile occupies 64 bytes when using 8-bit pixels
|
||||||
|
tileOffset = tile&0x3FFF;
|
||||||
|
tileOffset *= 64;
|
||||||
|
tileOffset /= 4;
|
||||||
|
|
||||||
|
// Upper color bits
|
||||||
|
palette = tile&0x7F00;
|
||||||
|
|
||||||
|
// Draw 4 pixels at a time
|
||||||
|
pattern = vram[tileOffset+tileLine];
|
||||||
|
bitPos = 24;
|
||||||
|
for (int i = 0; (i < 4) && (i < numPixels); i++)
|
||||||
|
{
|
||||||
|
buf[offset] = pal[((pattern>>bitPos)&0xFF) | palette];
|
||||||
|
++offset;
|
||||||
|
bitPos -= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
pattern = vram[tileOffset+tileLine+1];
|
||||||
|
bitPos = 24;
|
||||||
|
for (int i = 0; (i < 4) && (i < numPixels); i++)
|
||||||
|
{
|
||||||
|
buf[offset] = pal[((pattern>>bitPos)&0xFF) | palette];
|
||||||
|
++offset;
|
||||||
|
bitPos -= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Layer Rendering
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DrawCompleteLayer():
|
||||||
|
*
|
||||||
|
* Updates the complete layer.
|
||||||
|
*/
|
||||||
|
void CRender2D::DrawCompleteLayer(int layerNum, const UINT16 *nameTableBase)
|
||||||
|
{
|
||||||
|
UINT32 *dest = surf; // destination surface to write to
|
||||||
|
UINT32 *lineBufferPri = &surf[512*496]; // line buffer for primary and alternate layer
|
||||||
|
UINT32 *lineBufferAlt = &surf[512*497];
|
||||||
|
UINT32 *buf;
|
||||||
|
const UINT16 *maskTable; // pointer to start of mask table
|
||||||
|
const UINT16 *hScrollTablePri, *hScrollTableAlt; // pointers to line scroll tables
|
||||||
|
const UINT16 *nameTablePri = nameTableBase; // primary (this layer) name table
|
||||||
|
const UINT16 *nameTableAlt = &nameTableBase[64*64]; // alternate layer's name table
|
||||||
|
const UINT16 *nameTable;
|
||||||
|
int colorDepthPri, colorDepthAlt; // primary and alternate layer color depths
|
||||||
|
int hScrollPri, hScrollAlt; // primary and alternate layer scroll offsets
|
||||||
|
int vScrollPri, vScrollAlt;
|
||||||
|
int hFullScrollPri, hFullScrollAlt; // full-screen horizontal scroll values (from registers)
|
||||||
|
int vOffset; // vertical pixel offset within tile
|
||||||
|
int tx, i, j;
|
||||||
|
BOOL lineScrollPri, lineScrollAlt; // line scrolling enable/disable
|
||||||
|
UINT16 mask;
|
||||||
|
|
||||||
|
// Determine layer color depths (1 if 4-bit, 0 if 8-bit)
|
||||||
|
colorDepthPri = regs[0x20/4] & (1<<(12+layerNum*2));
|
||||||
|
colorDepthAlt = regs[0x20/4] & (1<<(12+layerNum*2+1));
|
||||||
|
|
||||||
|
// Line scroll tables
|
||||||
|
hScrollTablePri = (UINT16 *) &vram[(0xF6000+layerNum*2*0x400)/4];
|
||||||
|
hScrollTableAlt = (UINT16 *) &vram[(0xF6000+layerNum*2*0x400+0x400)/4];
|
||||||
|
|
||||||
|
// Get correct offset into mask table
|
||||||
|
maskTable = (UINT16 *) &vram[0xF7000/4];
|
||||||
|
if (layerNum == 0)
|
||||||
|
++maskTable; // little endian, layer 0 is second word in each pair
|
||||||
|
|
||||||
|
// Load horizontal full-screen scroll values and scroll mode
|
||||||
|
hFullScrollPri = regs[0x60/4+layerNum*2]&0x3FF;
|
||||||
|
hFullScrollAlt = regs[0x60/4+layerNum*2+1]&0x3FF;
|
||||||
|
lineScrollPri = regs[0x60/4+layerNum*2]&0x8000;
|
||||||
|
lineScrollAlt = regs[0x60/4+layerNum*2+1]&0x8000;
|
||||||
|
|
||||||
|
// Load vertical scroll values
|
||||||
|
vScrollPri = (regs[0x60/4+layerNum*2]>>16)&0x1FF;
|
||||||
|
vScrollAlt = (regs[0x60/4+layerNum*2+1]>>16)&0x1FF;
|
||||||
|
|
||||||
|
// Iterate over all displayed lines
|
||||||
|
for (int y = 0; y < 384; y++)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Draw all tiles from primary layer first. Horizontal scrolling is not
|
||||||
|
* applied yet, but vertical scrolling is taken into account. An entire
|
||||||
|
* 512-pixel line is rendered so that it can be scrolled during mixing.
|
||||||
|
*/
|
||||||
|
nameTable = &nameTablePri[(64*((y+vScrollPri)/8)) & 0xFFF]; // clamp to 64x64=0x1000
|
||||||
|
vOffset = (y+vScrollPri)&7;
|
||||||
|
buf = lineBufferPri; // output to primary line buffer
|
||||||
|
for (tx = 0; tx < 64; tx += 4) // 4 tiles at a time (for masking)
|
||||||
|
{
|
||||||
|
if (colorDepthPri) //TODO: move this test outside of loop
|
||||||
|
{
|
||||||
|
DrawTileLine4BitNoClip(buf, nameTable[1], vOffset);
|
||||||
|
buf += 8;
|
||||||
|
DrawTileLine4BitNoClip(buf, nameTable[0], vOffset);
|
||||||
|
buf += 8;
|
||||||
|
DrawTileLine4BitNoClip(buf, nameTable[3], vOffset);
|
||||||
|
buf += 8;
|
||||||
|
DrawTileLine4BitNoClip(buf, nameTable[2], vOffset);
|
||||||
|
buf += 8;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DrawTileLine8BitNoClip(buf, nameTable[1], vOffset);
|
||||||
|
buf += 8;
|
||||||
|
DrawTileLine8BitNoClip(buf, nameTable[0], vOffset);
|
||||||
|
buf += 8;
|
||||||
|
DrawTileLine8BitNoClip(buf, nameTable[3], vOffset);
|
||||||
|
buf += 8;
|
||||||
|
DrawTileLine8BitNoClip(buf, nameTable[2], vOffset);
|
||||||
|
buf += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next set of 4 tiles
|
||||||
|
nameTable += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Draw the alternate layer wherever the primary layer was masked
|
||||||
|
*/
|
||||||
|
nameTable = &nameTableAlt[(64*((y+vScrollAlt)/8))&0xFFF];
|
||||||
|
vOffset = (y+vScrollAlt)&7;
|
||||||
|
buf = lineBufferAlt; // output to alternate line buffer
|
||||||
|
for (tx = 0; tx < 64; tx += 4) // 4 tiles at a time (for masking)
|
||||||
|
{
|
||||||
|
if (colorDepthAlt) //TODO: move this test outside of loop
|
||||||
|
{
|
||||||
|
DrawTileLine4BitNoClip(buf, nameTable[1], vOffset);
|
||||||
|
buf += 8;
|
||||||
|
DrawTileLine4BitNoClip(buf, nameTable[0], vOffset);
|
||||||
|
buf += 8;
|
||||||
|
DrawTileLine4BitNoClip(buf, nameTable[3], vOffset);
|
||||||
|
buf += 8;
|
||||||
|
DrawTileLine4BitNoClip(buf, nameTable[2], vOffset);
|
||||||
|
buf += 8;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DrawTileLine8BitNoClip(buf, nameTable[1], vOffset);
|
||||||
|
buf += 8;
|
||||||
|
DrawTileLine8BitNoClip(buf, nameTable[0], vOffset);
|
||||||
|
buf += 8;
|
||||||
|
DrawTileLine8BitNoClip(buf, nameTable[3], vOffset);
|
||||||
|
buf += 8;
|
||||||
|
DrawTileLine8BitNoClip(buf, nameTable[2], vOffset);
|
||||||
|
buf += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next set of 4 tiles
|
||||||
|
nameTable += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mix the two layers into the current line under control of the
|
||||||
|
* stencil mask, applying scrolling in the process.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Load horizontal scroll values
|
||||||
|
if (lineScrollPri)
|
||||||
|
hScrollPri = hScrollTablePri[y];
|
||||||
|
else
|
||||||
|
hScrollPri = hFullScrollPri;
|
||||||
|
if (lineScrollAlt)
|
||||||
|
hScrollAlt = hScrollTableAlt[y];
|
||||||
|
else
|
||||||
|
hScrollAlt = hFullScrollAlt;
|
||||||
|
|
||||||
|
// Mix first 60 tiles (4 at a time)
|
||||||
|
mask = *maskTable;
|
||||||
|
i = hScrollPri&511; // primary line index
|
||||||
|
j = hScrollAlt&511; // alternate line index
|
||||||
|
for (tx = 0; tx < 60; tx += 4)
|
||||||
|
{
|
||||||
|
if ((mask&0x8000)) // copy tiles from primary layer
|
||||||
|
{
|
||||||
|
if (i <= (512-32)) // safe to use memcpy for fast blit?
|
||||||
|
{
|
||||||
|
memcpy(dest, &lineBufferPri[i], 32*sizeof(UINT32));
|
||||||
|
i += 32;
|
||||||
|
dest += 32;
|
||||||
|
}
|
||||||
|
else // slow copy, wrap line boundary
|
||||||
|
{
|
||||||
|
for (int k = 0; k < 32; k++)
|
||||||
|
{
|
||||||
|
i &= 511;
|
||||||
|
*dest++ = lineBufferPri[i++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
j += 32; // update alternate pointer as well
|
||||||
|
}
|
||||||
|
else // copy tiles from alternate layer
|
||||||
|
{
|
||||||
|
if (j <= (512-32))
|
||||||
|
{
|
||||||
|
memcpy(dest, &lineBufferAlt[j], 32*sizeof(UINT32));
|
||||||
|
j += 32;
|
||||||
|
dest += 32;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int k = 0; k < 32; k++)
|
||||||
|
{
|
||||||
|
j &= 511;
|
||||||
|
*dest++ = lineBufferAlt[j++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 32; // update primary
|
||||||
|
}
|
||||||
|
|
||||||
|
mask <<= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mix last two tiles
|
||||||
|
if ((mask&0x8000)) // copy tiles from primary layer
|
||||||
|
{
|
||||||
|
for (int k = 0; k < 16; k++)
|
||||||
|
{
|
||||||
|
i &= 511;
|
||||||
|
*dest++ = lineBufferPri[i++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // copy from alternate
|
||||||
|
{
|
||||||
|
for (int k = 0; k < 16; k++)
|
||||||
|
{
|
||||||
|
j &= 511;
|
||||||
|
*dest++ = lineBufferAlt[j++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next line
|
||||||
|
maskTable += 2; // next mask line
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload
|
||||||
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 496, 384, GL_RGBA, GL_UNSIGNED_BYTE, surf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DrawRect():
|
||||||
|
*
|
||||||
|
* Draws a rectangular portion of the given layer and uploads it. Scrolling is
|
||||||
|
* applied but the result must be clipped against the rectangular window
|
||||||
|
* defined here by tileX, tileY, tileW, and tileH.
|
||||||
|
*
|
||||||
|
* Clipping for the right side is not checked, which will always work as long
|
||||||
|
* as the X and Y tile positions never exceed 61 and 47, respectively (the
|
||||||
|
* dimensions of the physical display; there is border space because the
|
||||||
|
* layers are 64x64).
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* layerNum Layer number (0 or 1).
|
||||||
|
* nameTableBase Pointer to the layer's name table.
|
||||||
|
* tileX Position of upper-left corner of rectangle on the
|
||||||
|
* screen, in units of 8-pixel tiles (0-61).
|
||||||
|
* tileY "" (0-47)
|
||||||
|
* tileW Width of rectangular region in tiles.
|
||||||
|
* tileH "" (height)
|
||||||
|
*/
|
||||||
|
void CRender2D::DrawRect(int layerNum, const UINT16 *nameTableBase, int tileX, int tileY, int tileW, int tileH)
|
||||||
|
{
|
||||||
|
UINT32 *dest = surf; // destination surface to write to
|
||||||
|
const UINT16 *maskTable; // pointer to start of mask table
|
||||||
|
const UINT16 *hScrollTablePri, *hScrollTableAlt; // pointers to line scroll tables
|
||||||
|
const UINT16 *nameTablePri = nameTableBase; // primary (this layer) name table
|
||||||
|
const UINT16 *nameTableAlt = &nameTableBase[64*64]; // alternate layer's name table
|
||||||
|
const UINT16 *nameTable;
|
||||||
|
int colorDepthPri, colorDepthAlt; // primary and alternate layer color depths
|
||||||
|
int hScrollPri, hScrollAlt; // primary and alternate layer scroll offsets
|
||||||
|
int vScrollPri, vScrollAlt;
|
||||||
|
int hFullScrollPri, hFullScrollAlt; // full-screen horizontal scroll values (from registers)
|
||||||
|
int hOffset, vOffset; // pixel offsets
|
||||||
|
int ntOffset; // offset in name table
|
||||||
|
int tx;
|
||||||
|
BOOL lineScrollPri, lineScrollAlt; // line scrolling enable/disable
|
||||||
|
UINT16 mask;
|
||||||
|
|
||||||
|
// Determine layer color depths (1 if 4-bit, 0 if 8-bit)
|
||||||
|
colorDepthPri = regs[0x20/4] & (1<<(12+layerNum*2));
|
||||||
|
colorDepthAlt = regs[0x20/4] & (1<<(12+layerNum*2+1));
|
||||||
|
|
||||||
|
// Line scroll tables
|
||||||
|
hScrollTablePri = (UINT16 *) &vram[(0xF6000+layerNum*2*0x400)/4];
|
||||||
|
hScrollTableAlt = (UINT16 *) &vram[(0xF6000+layerNum*2*0x400+0x400)/4];
|
||||||
|
|
||||||
|
// Get correct offset into mask table
|
||||||
|
maskTable = (UINT16 *) &vram[0xF7000/4];
|
||||||
|
if (layerNum == 0)
|
||||||
|
++maskTable; // little endian, layer 0 is second word in each pair
|
||||||
|
|
||||||
|
// Load horizontal full-screen scroll values and scroll mode
|
||||||
|
hFullScrollPri = regs[0x60/4+layerNum*2]&0x3FF;
|
||||||
|
hFullScrollAlt = regs[0x60/4+layerNum*2+1]&0x3FF;
|
||||||
|
lineScrollPri = regs[0x60/4+layerNum*2]&0x8000;
|
||||||
|
lineScrollAlt = regs[0x60/4+layerNum*2+1]&0x8000;
|
||||||
|
|
||||||
|
// Load vertical scroll values
|
||||||
|
vScrollPri = (regs[0x60/4+layerNum*2]>>16)&0x3FF;
|
||||||
|
vScrollAlt = (regs[0x60/4+layerNum*2+1]>>16)&0x3FF;
|
||||||
|
|
||||||
|
// Iterate over actual line on screen
|
||||||
|
for (int y = tileY*8; y < (tileY+tileH)*8; y++)
|
||||||
|
{
|
||||||
|
|
||||||
|
// Load horizontal scroll values
|
||||||
|
if (lineScrollPri)
|
||||||
|
hScrollPri = hScrollTablePri[y];
|
||||||
|
else
|
||||||
|
hScrollPri = hFullScrollPri;
|
||||||
|
if (lineScrollAlt)
|
||||||
|
hScrollAlt = hScrollTableAlt[y];
|
||||||
|
else
|
||||||
|
hScrollAlt = hFullScrollAlt;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Draw all tiles from primary layer first
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Compute scroll offsets into name table and destination
|
||||||
|
hOffset = -(hScrollPri&7);
|
||||||
|
vOffset = (y+vScrollPri)&7;
|
||||||
|
ntOffset = tileX+hScrollPri/8;
|
||||||
|
|
||||||
|
// Advance name table to our line (prior to h-scrolling)
|
||||||
|
nameTable = &nameTablePri[64*((y+vScrollPri)/8) & 0xFFF]; // clamp to 64x64=0x1000
|
||||||
|
|
||||||
|
// Each bit in the mask table corresponds to 4 tiles
|
||||||
|
mask = maskTable[y*2];
|
||||||
|
|
||||||
|
// Render a line!
|
||||||
|
//TODO: add one if scrolling
|
||||||
|
for (tx = 0; tx < tileW; tx++)
|
||||||
|
{
|
||||||
|
if ( ((mask<<((tileX+tx)/4)) & 0x8000) )
|
||||||
|
{
|
||||||
|
if (colorDepthPri)
|
||||||
|
DrawTileLine4Bit(dest, hOffset, nameTable[(ntOffset^1)&63], vOffset); // make sure ^1 belongs inside parenthesis...
|
||||||
|
else
|
||||||
|
DrawTileLine8Bit(dest, hOffset, nameTable[(ntOffset^1)&63], vOffset);
|
||||||
|
}
|
||||||
|
hOffset += 8;
|
||||||
|
ntOffset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When scrolling, extra tile must be rendered at right edge of region
|
||||||
|
if ( ((mask<<((tileX+tx)/4)) & 0x8000) ) // re-use the last mask bit (mask doesn't scroll)
|
||||||
|
{
|
||||||
|
if (colorDepthPri)
|
||||||
|
DrawTileLine4BitRightClip(dest, hOffset, nameTable[(ntOffset^1)&63], vOffset, hScrollPri&7);
|
||||||
|
else
|
||||||
|
DrawTileLine8BitRightClip(dest, hOffset, nameTable[(ntOffset^1)&63], vOffset, hScrollPri&7);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Draw the alternate layer wherever the primary layer was masked
|
||||||
|
*/
|
||||||
|
|
||||||
|
hOffset = -(hScrollAlt&7);
|
||||||
|
vOffset = (y+vScrollAlt)&7;
|
||||||
|
ntOffset = tileX+hScrollAlt/8;
|
||||||
|
nameTable = &nameTableAlt[64*((y+vScrollAlt)/8)];
|
||||||
|
mask = maskTable[y*2];
|
||||||
|
for (tx = 0; tx < tileW; tx++)
|
||||||
|
{
|
||||||
|
if (0 == ((mask<<((tileX+tx)/4)) & 0x8000))
|
||||||
|
{
|
||||||
|
if (colorDepthAlt)
|
||||||
|
DrawTileLine4Bit(dest, hOffset, nameTable[(ntOffset^1)&63], vOffset);
|
||||||
|
else
|
||||||
|
DrawTileLine8Bit(dest, hOffset, nameTable[(ntOffset^1)&63], vOffset);
|
||||||
|
}
|
||||||
|
hOffset += 8;
|
||||||
|
ntOffset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When scrolling, extra tile must be rendered at right edge of region
|
||||||
|
if (0 == ((mask<<((tileX+tx)/4)) & 0x8000)) // re-use the last mask bit (mask doesn't scroll)
|
||||||
|
{
|
||||||
|
if (colorDepthAlt)
|
||||||
|
DrawTileLine4BitRightClip(dest, hOffset, nameTable[(ntOffset^1)&63], vOffset, hScrollAlt&7);
|
||||||
|
else
|
||||||
|
DrawTileLine8BitRightClip(dest, hOffset, nameTable[(ntOffset^1)&63], vOffset, hScrollAlt&7);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next line
|
||||||
|
dest += tileW*8; // image surface is only as wide as the rectangle we're updating
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload
|
||||||
|
glTexSubImage2D(GL_TEXTURE_2D, 0, tileX*8, tileY*8, tileW*8, tileH*8, GL_RGBA, GL_UNSIGNED_BYTE, surf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updates any changed portions of a layer
|
||||||
|
void CRender2D::UpdateLayer(int layerNum)
|
||||||
|
{
|
||||||
|
glBindTexture(GL_TEXTURE_2D, texID[layerNum]);
|
||||||
|
|
||||||
|
allDirty = TRUE;
|
||||||
|
if (allDirty)
|
||||||
|
{
|
||||||
|
// If everything is dirty, update the whole thing at once
|
||||||
|
DrawCompleteLayer(layerNum, (UINT16 *) &vram[(0xF8000+layerNum*2*0x2000)/4]);
|
||||||
|
//DrawRect(layerNum, (UINT16 *) &vram[(0xF8000+layerNum*2*0x2000)/4], 0, 0, 62, 48);
|
||||||
|
memset(dirty, 0, sizeof(dirty));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Otherwise, for now, use a dumb approach that updates each rectangle individually
|
||||||
|
for (int y = 0; y < 64/DIRTY_RECT_HEIGHT; y++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < 48/DIRTY_RECT_WIDTH; x++)
|
||||||
|
{
|
||||||
|
if (dirty[layerNum][y][x])
|
||||||
|
{
|
||||||
|
DrawRect(layerNum, (UINT16 *) &vram[(0xF8000+layerNum*2*0x2000)/4], x*DIRTY_RECT_WIDTH, y*DIRTY_RECT_HEIGHT, DIRTY_RECT_WIDTH, DIRTY_RECT_HEIGHT);
|
||||||
|
dirty[layerNum][y][x] = 0; // not dirty anymore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Frame Display Functions
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
// Draws a layer to the screen
|
||||||
|
void CRender2D::DisplayLayer(int layerNum, GLfloat z)
|
||||||
|
{
|
||||||
|
glBindTexture(GL_TEXTURE_2D, texID[layerNum]);
|
||||||
|
glBegin(GL_QUADS);
|
||||||
|
glTexCoord2f(0.0f/512.0f, 0.0f); glVertex3f(0.0f, 0.0f, z);
|
||||||
|
glTexCoord2f(496.0f/512.0f, 0.0f); glVertex3f(1.0f, 0.0f, z);
|
||||||
|
glTexCoord2f(496.0f/512.0f, 384.0f/512.0f); glVertex3f(1.0f, 1.0f, z);
|
||||||
|
glTexCoord2f(0.0f/512.0f, 384.0f/512.0f); glVertex3f(0.0f, 1.0f, z);
|
||||||
|
glEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up viewport and OpenGL state for 2D rendering (sets up blending function but disables blending)
|
||||||
|
void CRender2D::Setup2D(void)
|
||||||
|
{
|
||||||
|
// Set up the viewport and orthogonal projection
|
||||||
|
glViewport(xOffs, yOffs, xPixels, yPixels);
|
||||||
|
glMatrixMode(GL_PROJECTION);
|
||||||
|
glLoadIdentity();
|
||||||
|
gluOrtho2D(0.0, 1.0, 1.0, 0.0);
|
||||||
|
glMatrixMode(GL_MODELVIEW);
|
||||||
|
glLoadIdentity();
|
||||||
|
|
||||||
|
// Enable texture mapping and blending
|
||||||
|
glEnable(GL_TEXTURE_2D);
|
||||||
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
||||||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // alpha of 1.0 is opaque, 0 is transparent
|
||||||
|
glDisable(GL_BLEND);
|
||||||
|
|
||||||
|
// Disable Z-buffering
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
|
// Shader program
|
||||||
|
glUseProgram(shaderProgram);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert color offset register data to RGB
|
||||||
|
void CRender2D::ColorOffset(GLfloat colorOffset[3], UINT32 reg)
|
||||||
|
{
|
||||||
|
INT8 ir, ig, ib;
|
||||||
|
|
||||||
|
ib = (reg>>16)&0xFF;
|
||||||
|
ig = (reg>>8)&0xFF;
|
||||||
|
ir = (reg>>0)&0xFF;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Uncertain how these should be interpreted. It appears to be signed,
|
||||||
|
* which means the values range from -128 to +127. The division by 128
|
||||||
|
* normalizes this to roughly -1,+1.
|
||||||
|
*/
|
||||||
|
colorOffset[0] = (GLfloat) ir * (1.0f/128.0f);
|
||||||
|
colorOffset[1] = (GLfloat) ig * (1.0f/128.0f);
|
||||||
|
colorOffset[2] = (GLfloat) ib * (1.0f/128.0f);
|
||||||
|
//printf("%08X -> %g,%g,%g\n", reg, colorOffset[2], colorOffset[1], colorOffset[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bottom layers
|
||||||
|
void CRender2D::BeginFrame(void)
|
||||||
|
{
|
||||||
|
GLfloat colorOffset[3];
|
||||||
|
|
||||||
|
// Update all layers
|
||||||
|
for (int i = 0; i < 2; i++)
|
||||||
|
{
|
||||||
|
UpdateLayer(i);
|
||||||
|
}
|
||||||
|
allDirty = FALSE;
|
||||||
|
|
||||||
|
// Draw bottom layer
|
||||||
|
Setup2D();
|
||||||
|
ColorOffset(colorOffset, regs[0x44/4]);
|
||||||
|
glUniform3fv(colorOffsetLoc, 1, colorOffset);
|
||||||
|
DisplayLayer(1, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Top layers
|
||||||
|
void CRender2D::EndFrame(void)
|
||||||
|
{
|
||||||
|
GLfloat colorOffset[3];
|
||||||
|
|
||||||
|
// Draw top layer
|
||||||
|
Setup2D();
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
ColorOffset(colorOffset, regs[0x40/4]);
|
||||||
|
glUniform3fv(colorOffsetLoc, 1, colorOffset);
|
||||||
|
DisplayLayer(0, -0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Emulation Callbacks
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void CRender2D::WriteVRAM(unsigned addr, UINT32 data)
|
||||||
|
{
|
||||||
|
unsigned color;
|
||||||
|
UINT8 r, g, b, a;
|
||||||
|
|
||||||
|
if (vram[addr/4] == data) // do nothing if no changes
|
||||||
|
return;
|
||||||
|
|
||||||
|
// For now, mark everything as dirty
|
||||||
|
allDirty = TRUE;
|
||||||
|
|
||||||
|
// Palette
|
||||||
|
if (addr >= 0x100000)
|
||||||
|
{
|
||||||
|
color = (addr-0x100000)/4; // color index
|
||||||
|
a = 0xFF * ((data>>15)&1); // decode the RGBA (make alpha 0xFF or 0x00)
|
||||||
|
a = ~a; // invert it (set on Model 3 means clear pixel)
|
||||||
|
if ((data&0x8000))
|
||||||
|
r = g = b = 0;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
b = (data>>7)&0xF8;
|
||||||
|
g = (data>>2)&0xF8;
|
||||||
|
r = (data<<3)&0xF8;
|
||||||
|
}
|
||||||
|
pal[color] = (a<<24)|(b<<16)|(g<<8)|r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Configuration, Initialization, and Shutdown
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void CRender2D::AttachRegisters(const UINT32 *regPtr)
|
||||||
|
{
|
||||||
|
regs = regPtr;
|
||||||
|
DebugLog("Render2D attached registers\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CRender2D::AttachVRAM(const UINT8 *vramPtr)
|
||||||
|
{
|
||||||
|
vram = (UINT32 *) vramPtr;
|
||||||
|
DebugLog("Render2D attached VRAM\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MEMORY_POOL_SIZE (512*512*4+0x20000)
|
||||||
|
|
||||||
|
BOOL CRender2D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes)
|
||||||
|
{
|
||||||
|
float memSizeMB = (float)MEMORY_POOL_SIZE/(float)0x100000;
|
||||||
|
|
||||||
|
// Load shaders
|
||||||
|
if (OKAY != LoadShaderProgram(&shaderProgram,&vertexShader,&fragmentShader,NULL,NULL,vertexShaderSource,fragmentShaderSource))
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
// Get locations of the uniforms
|
||||||
|
glUseProgram(shaderProgram); // bind program
|
||||||
|
textureMapLoc = glGetUniformLocation(shaderProgram, "textureMap");
|
||||||
|
glUniform1i(textureMapLoc,0); // attach it to texture unit 0
|
||||||
|
colorOffsetLoc = glGetUniformLocation(shaderProgram, "colorOffset");
|
||||||
|
|
||||||
|
// Allocate memory for layer surfaces and palette
|
||||||
|
memoryPool = new(std::nothrow) UINT8[MEMORY_POOL_SIZE];
|
||||||
|
if (NULL == memoryPool)
|
||||||
|
return ErrorLog("Insufficient memory for tile layer surfaces (need %1.1f MB).", memSizeMB);
|
||||||
|
memset(memoryPool,0,MEMORY_POOL_SIZE);
|
||||||
|
|
||||||
|
// Set up pointers to memory regions
|
||||||
|
surf = (UINT32 *) memoryPool;
|
||||||
|
pal = (UINT32 *) &memoryPool[512*512*4];
|
||||||
|
|
||||||
|
// Resolution
|
||||||
|
xPixels = xRes;
|
||||||
|
yPixels = yRes;
|
||||||
|
xOffs = xOffset;
|
||||||
|
yOffs = yOffset;
|
||||||
|
|
||||||
|
// Clear textures and dirty rectangles (all memory)
|
||||||
|
memset(memoryPool, 0, MEMORY_POOL_SIZE);
|
||||||
|
memset(dirty, 0, sizeof(dirty));
|
||||||
|
allDirty = TRUE;
|
||||||
|
|
||||||
|
// Create textures
|
||||||
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||||
|
glGenTextures(2, texID);
|
||||||
|
for (int i = 0; i < 2; i++)
|
||||||
|
{
|
||||||
|
glBindTexture(GL_TEXTURE_2D, texID[i]);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 512, 0, GL_RGBA, GL_UNSIGNED_BYTE, surf);
|
||||||
|
if (glGetError() != GL_NO_ERROR)
|
||||||
|
return ErrorLog("OpenGL was unable to provide 512x512-texel texture maps for tile map layers.");
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugLog("Render2D initialized (allocated %1.1f MB)\n", memSizeMB);
|
||||||
|
return OKAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
CRender2D::CRender2D(void)
|
||||||
|
{
|
||||||
|
xPixels = 496;
|
||||||
|
yPixels = 384;
|
||||||
|
xOffs = 0;
|
||||||
|
yOffs = 0;
|
||||||
|
|
||||||
|
memoryPool = NULL;
|
||||||
|
vram = NULL;
|
||||||
|
surf = NULL;
|
||||||
|
|
||||||
|
DebugLog("Built Render2D\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
CRender2D::~CRender2D(void)
|
||||||
|
{
|
||||||
|
DestroyShaderProgram(shaderProgram,vertexShader,fragmentShader);
|
||||||
|
glDeleteTextures(2, texID);
|
||||||
|
|
||||||
|
if (memoryPool != NULL)
|
||||||
|
{
|
||||||
|
delete [] memoryPool;
|
||||||
|
memoryPool = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
surf = NULL;
|
||||||
|
vram = NULL;
|
||||||
|
|
||||||
|
DebugLog("Destroyed Render2D\n");
|
||||||
|
}
|
177
Graphics/Render2D.h
Normal file
177
Graphics/Render2D.h
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Render2D.h
|
||||||
|
*
|
||||||
|
* Header file defining the CRender2D class: OpenGL tile generator graphics.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDED_RENDER2D_H
|
||||||
|
#define INCLUDED_RENDER2D_H
|
||||||
|
|
||||||
|
#include "Pkgs/glew.h"
|
||||||
|
|
||||||
|
|
||||||
|
// Dirty rectangle size, must be multiples of 4 because masking code relies on this
|
||||||
|
#define DIRTY_RECT_WIDTH 8 // width of a dirty rectangle in 8-pixel tiles (multiples of 4, divisible into 64)
|
||||||
|
#define DIRTY_RECT_HEIGHT 8 // height (multiples of 4, divisible into 48)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CRender2D:
|
||||||
|
*
|
||||||
|
* Tile generator graphics engine. This must be constructed and initialized
|
||||||
|
* before being attached to any objects that want to make use of it. Apart from
|
||||||
|
* the constructor, all members assume that a global GL device
|
||||||
|
* context is available and that GL functions may be called.
|
||||||
|
*/
|
||||||
|
class CRender2D
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
* BeginFrame(void):
|
||||||
|
*
|
||||||
|
* Prepare to render a new frame. Must be called once per frame prior to
|
||||||
|
* drawing anything.
|
||||||
|
*/
|
||||||
|
void BeginFrame(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* EndFrame(void):
|
||||||
|
*
|
||||||
|
* Signals the end of rendering for this frame. Must be called last during
|
||||||
|
* the frame.
|
||||||
|
*/
|
||||||
|
void EndFrame(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WriteVRAM(addr, data):
|
||||||
|
*
|
||||||
|
* Indicates what will be written next to the tile generator's RAM. The
|
||||||
|
* VRAM address must not have yet been updated, to allow the renderer to
|
||||||
|
* check for changes. Data is accepted in the same form as the tile
|
||||||
|
* generator: the MSB is what was written to addr+3. This function is
|
||||||
|
* intended to facilitate on-the-fly decoding of tiles and palette data.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* addr Address in tile generator RAM. Caller must ensure it is
|
||||||
|
* clamped to the range 0x000000 to 0x11FFFF because this
|
||||||
|
* function does not.
|
||||||
|
* data The data to write.
|
||||||
|
*/
|
||||||
|
void WriteVRAM(unsigned addr, UINT32 data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AttachRegisters(regPtr):
|
||||||
|
*
|
||||||
|
* Attaches tile generator registers. This must be done prior to any
|
||||||
|
* rendering otherwise the program may crash with an access violation.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* regPtr Pointer to the base of the tile generator registers.
|
||||||
|
* There are assumed to be 64 in all.
|
||||||
|
*/
|
||||||
|
void AttachRegisters(const UINT32 *regPtr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AttachVRAM(vramPtr):
|
||||||
|
*
|
||||||
|
* Attaches tile generator RAM. This must be done prior to any rendering
|
||||||
|
* otherwise the program may crash with an access violation.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* vramPtr Pointer to the base of the tile generator RAM (0x120000
|
||||||
|
* bytes). VRAM is assumed to be in little endian format.
|
||||||
|
*/
|
||||||
|
void AttachVRAM(const UINT8 *vramPtr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Init(xOffset, yOffset, xRes, yRes);
|
||||||
|
*
|
||||||
|
* One-time initialization of the context. Must be called before any other
|
||||||
|
* members (meaning it should be called even before being attached to any
|
||||||
|
* other objects that want to use it).
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* xOffset X offset within OpenGL display surface in pixels.
|
||||||
|
* yOffset Y offset.
|
||||||
|
* xRes Horizontal resolution of display surface in pixels.
|
||||||
|
* yRes Vertical resolution.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* OKAY is successful, otherwise FAILED if a non-recoverable error
|
||||||
|
* occurred. Prints own error messages.
|
||||||
|
*/
|
||||||
|
BOOL Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CRender2D(void):
|
||||||
|
* ~CRender2D(void):
|
||||||
|
*
|
||||||
|
* Constructor and destructor.
|
||||||
|
*/
|
||||||
|
CRender2D(void);
|
||||||
|
~CRender2D(void);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Private member functions
|
||||||
|
void DrawTileLine8BitNoClip(UINT32 *buf, UINT16 tile, int tileLine);
|
||||||
|
void DrawTileLine4BitNoClip(UINT32 *buf, UINT16 tile, int tileLine);
|
||||||
|
void DrawTileLine4Bit(UINT32 *buf, int offset, UINT16 tile, int tileLine);
|
||||||
|
void DrawTileLine4BitRightClip(UINT32 *buf, int offset, UINT16 tile, int tileLine, int numPixels);
|
||||||
|
void DrawTileLine8Bit(UINT32 *buf, int offset, UINT16 tile, int tileLine);
|
||||||
|
void DrawTileLine8BitRightClip(UINT32 *buf, int offset, UINT16 tile, int tileLine, int numPixels);
|
||||||
|
void DrawCompleteLayer(int layerNum, const UINT16 *nameTableBase);
|
||||||
|
void DrawRect(int layerNum, const UINT16 *nameTableBase, int tileX, int tileY, int tileW, int tileH);
|
||||||
|
void UpdateLayer(int layerNum);
|
||||||
|
void DisplayLayer(int layerNum, GLfloat z);
|
||||||
|
void Setup2D(void);
|
||||||
|
void ColorOffset(GLfloat colorOffset[3], UINT32 reg);
|
||||||
|
|
||||||
|
// Data received from tile generator device object
|
||||||
|
const UINT32 *vram;
|
||||||
|
const UINT32 *regs;
|
||||||
|
|
||||||
|
// OpenGL data
|
||||||
|
GLuint texID[2]; // IDs for the 2 layer textures
|
||||||
|
unsigned xPixels, yPixels; // display surface resolution
|
||||||
|
unsigned xOffs, yOffs; // offset
|
||||||
|
|
||||||
|
// Shader programs and input data locations
|
||||||
|
GLuint shaderProgram; // shader program object
|
||||||
|
GLuint vertexShader; // vertex shader handle
|
||||||
|
GLuint fragmentShader; // fragment shader
|
||||||
|
GLuint textureMapLoc; // location of "textureMap" uniform
|
||||||
|
GLuint colorOffsetLoc; // uniform
|
||||||
|
|
||||||
|
|
||||||
|
// Dirty rectangles (non-zero indicates region is dirty)
|
||||||
|
UINT8 dirty[2][64/DIRTY_RECT_HEIGHT][48/DIRTY_RECT_WIDTH];
|
||||||
|
BOOL allDirty; // global dirty flag (forces everything to be updated)
|
||||||
|
|
||||||
|
// Buffers
|
||||||
|
UINT8 *memoryPool; // all memory is allocated here
|
||||||
|
UINT32 *surf; // 512x512x32bpp pixel surface
|
||||||
|
UINT32 *pal; // 0x20000 byte (32K colors) palette
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // INCLUDED_RENDER2D_H
|
1388
Graphics/Render3D.cpp
Normal file
1388
Graphics/Render3D.cpp
Normal file
File diff suppressed because it is too large
Load diff
412
Graphics/Render3D.h
Normal file
412
Graphics/Render3D.h
Normal file
|
@ -0,0 +1,412 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Render3D.h
|
||||||
|
*
|
||||||
|
* Header file defining the CRender3D class: OpenGL Real3D graphics engine.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDED_RENDER3D_H
|
||||||
|
#define INCLUDED_RENDER3D_H
|
||||||
|
|
||||||
|
#include "Pkgs/glew.h"
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Internal Definitions and Data Structures
|
||||||
|
|
||||||
|
NOTE: These should probably be moved inside the Render3D namespace at some
|
||||||
|
point.
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
// Model caches sort models by alpha (translucency) state
|
||||||
|
enum POLY_STATE
|
||||||
|
{
|
||||||
|
POLY_STATE_NORMAL = 0,
|
||||||
|
POLY_STATE_ALPHA
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Vertex
|
||||||
|
{
|
||||||
|
GLfloat x,y,z; // vertex
|
||||||
|
GLfloat n[3]; // normal X, Y, Z
|
||||||
|
GLfloat u,v; // texture U, V coordinates (in texels, relative to selected texture)
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Poly
|
||||||
|
{
|
||||||
|
Vertex Vert[4];
|
||||||
|
GLfloat n[3]; // polygon normal (used for backface culling)
|
||||||
|
POLY_STATE state; // alpha or normal?
|
||||||
|
unsigned numVerts; // triangle (3) or quad (4)
|
||||||
|
const UINT32 *header; // pointer to Real3D 7-word polygon header
|
||||||
|
};
|
||||||
|
|
||||||
|
// References to model polygons stored in a VBO
|
||||||
|
struct VBORef
|
||||||
|
{
|
||||||
|
unsigned index[2]; // index of model polygons in VBO
|
||||||
|
unsigned numVerts[2]; // number of vertices
|
||||||
|
unsigned lutIdx; // LUT index associated with this model (for fast LUT clearing)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Display list items: model instances and viewport settings
|
||||||
|
struct DisplayList
|
||||||
|
{
|
||||||
|
BOOL isViewport; // if true, this is a viewport node
|
||||||
|
|
||||||
|
union
|
||||||
|
{
|
||||||
|
// Viewport data
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
GLfloat projectionMatrix[4*4]; // projection matrix
|
||||||
|
GLfloat lightingParams[6]; // lighting parameters (see RenderViewport() and vertex shader)
|
||||||
|
GLfloat spotEllipse[4]; // spotlight ellipse (see RenderViewport())
|
||||||
|
GLfloat spotRange[2]; // Z range
|
||||||
|
GLfloat spotColor[3]; // color
|
||||||
|
GLfloat fogParams[5]; // fog parameters (...)
|
||||||
|
GLint x, y; // viewport coordinates (scaled and in OpenGL format)
|
||||||
|
GLint width, height; // viewport dimensions (scaled for display surface size)
|
||||||
|
} Viewport;
|
||||||
|
|
||||||
|
// Model data
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
GLfloat modelViewMatrix[4*4]; // model-view matrix
|
||||||
|
unsigned index; // index in VBO
|
||||||
|
unsigned numVerts; // number of vertices
|
||||||
|
} Model;
|
||||||
|
} Data;
|
||||||
|
|
||||||
|
DisplayList *next; // next display list item with the same state (alpha or non-alpha)
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ModelCache:
|
||||||
|
*
|
||||||
|
* A model cache tracks all models in a particular region (ie., VROM or polygon
|
||||||
|
* RAM). It contains a look-up table to quickly obtain VBO indices. Be careful
|
||||||
|
* when accessing the LUT, there are some special cases.
|
||||||
|
*
|
||||||
|
* If the model cache is marked dynamic, cached models may not necessarily be
|
||||||
|
* retained. Clearing the model cache is also much faster. The LUT entry for
|
||||||
|
* the last model cached will be valid, but because the LUT may not be
|
||||||
|
* cleared, one cannot assume a model exists because there is a LUT entry
|
||||||
|
* pointing to it. Always use NeedToCache() to determine whether caching is
|
||||||
|
* necessary before reading the LUT!
|
||||||
|
*/
|
||||||
|
struct ModelCache
|
||||||
|
{
|
||||||
|
// Cache type
|
||||||
|
BOOL dynamic;
|
||||||
|
|
||||||
|
// Vertex buffer object
|
||||||
|
unsigned vboMaxOffset; // size of VBO (in bytes)
|
||||||
|
unsigned vboCurOffset; // current offset in VBO (in bytes)
|
||||||
|
GLuint vboID; // OpenGL VBO handle
|
||||||
|
|
||||||
|
// Local vertex buffers (enough for a single model)
|
||||||
|
unsigned maxVertIdx; // size of each local vertex buffer (in vertices)
|
||||||
|
unsigned curVertIdx[2]; // current vertex index (in vertices)
|
||||||
|
GLfloat *verts[2];
|
||||||
|
|
||||||
|
// Array of cached models
|
||||||
|
unsigned maxModels; // maximum number of models
|
||||||
|
unsigned numModels; // current number stored
|
||||||
|
VBORef *Models;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Look-Up Table:
|
||||||
|
*
|
||||||
|
* Can be accessed directly with a LUT index to determine the model index.
|
||||||
|
* However, it should not be used to determine whether a model needs to be
|
||||||
|
* cached. Use NeedToCache() instead. A valid index, for example, may still
|
||||||
|
* have to be re-cached if the model cache is dynamic (polygon RAM).
|
||||||
|
*/
|
||||||
|
unsigned lutSize; // number of elements in LUT
|
||||||
|
INT16 *lut; // stores indices into Models[] or -1 if not yet cached
|
||||||
|
|
||||||
|
// Display list
|
||||||
|
unsigned maxListSize; // maximum number of display list items
|
||||||
|
unsigned listSize; // number of items in display list
|
||||||
|
DisplayList *List; // holds all display list items
|
||||||
|
DisplayList *ListHead[2]; // heads of linked lists for each state
|
||||||
|
DisplayList *ListTail[2]; // current tail node for each state
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
CRender3D Class Definition
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CRender3D:
|
||||||
|
*
|
||||||
|
* 3D renderer. Lots of work to do here :)
|
||||||
|
*/
|
||||||
|
class CRender3D
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
* RenderFrame(void):
|
||||||
|
*
|
||||||
|
* Renders the complete scene database. Must be called between BeginFrame() and
|
||||||
|
* EndFrame(). This function traverses the scene database and builds up display
|
||||||
|
* lists.
|
||||||
|
*/
|
||||||
|
void RenderFrame(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* BeginFrame(void):
|
||||||
|
*
|
||||||
|
* Prepare to render a new frame. Must be called once per frame prior to
|
||||||
|
* drawing anything.
|
||||||
|
*/
|
||||||
|
void BeginFrame(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* EndFrame(void):
|
||||||
|
*
|
||||||
|
* Signals the end of rendering for this frame. Must be called last during
|
||||||
|
* the frame.
|
||||||
|
*/
|
||||||
|
void EndFrame(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* UploadTextures(x, y, width, height):
|
||||||
|
*
|
||||||
|
* Signals that a portion of texture RAM has been updated.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* x X position within texture RAM.
|
||||||
|
* y Y position within texture RAM.
|
||||||
|
* width Width of texture data in texels.
|
||||||
|
* height Height.
|
||||||
|
*/
|
||||||
|
void UploadTextures(unsigned x, unsigned y, unsigned width, unsigned height);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AttachMemory(cullingRAMLoPtr, cullingRAMHiPtr, polyRAMPtr, vromPtr,
|
||||||
|
* textureRAMPtr):
|
||||||
|
*
|
||||||
|
* Attaches RAM and ROM areas. This must be done prior to any rendering
|
||||||
|
* otherwise the program may crash with an access violation.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* cullingRAMLoPtr Pointer to low culling RAM (4 MB).
|
||||||
|
* cullingRAMHiPtr Pointer to high culling RAM (1 MB).
|
||||||
|
* polyRAMPtr Pointer to polygon RAM (4 MB).
|
||||||
|
* vromPtr Pointer to video ROM (64 MB).
|
||||||
|
* textureRAMPtr Pointer to texture RAM (8 MB).
|
||||||
|
*/
|
||||||
|
void AttachMemory(const UINT32 *cullingRAMLoPtr,
|
||||||
|
const UINT32 *cullingRAMHiPtr, const UINT32 *polyRAMPtr,
|
||||||
|
const UINT32 *vromPtr, const UINT16 *textureRAMPtr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SetStep(stepID):
|
||||||
|
*
|
||||||
|
* Sets the Model 3 hardware stepping, which also determines the Real3D
|
||||||
|
* functionality. The default is Step 1.0. This should be called prior to
|
||||||
|
* any other emulation functions and after Init().
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* stepID 0x10 for Step 1.0, 0x15 for Step 1.5, 0x20 for Step 2.0,
|
||||||
|
* or 0x21 for Step 2.1. Anything else defaults to 1.0.
|
||||||
|
*/
|
||||||
|
void SetStep(int stepID);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Init(xOffset, yOffset, xRes, yRes, vsFile, fsFile):
|
||||||
|
*
|
||||||
|
* One-time initialization of the context. Must be called before any other
|
||||||
|
* members (meaning it should be called even before being attached to any
|
||||||
|
* other objects that want to use it).
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* xOffset X offset of display surface in pixels (in case resolution
|
||||||
|
* is smaller than the true display surface).
|
||||||
|
* yOffset Y offset.
|
||||||
|
* xRes Horizontal resolution of display surface in pixels.
|
||||||
|
* yRes Vertical resolution.
|
||||||
|
* vsFile External vertex shader path. If NULL, the internal
|
||||||
|
* shader is used.
|
||||||
|
* fsFile External fragment shader path. If NULL, the internal
|
||||||
|
* shader is used.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* OKAY is successful, otherwise FAILED if a non-recoverable error
|
||||||
|
* occurred. Any allocated memory will not be freed until the
|
||||||
|
* destructor is called. Prints own error messages.
|
||||||
|
*/
|
||||||
|
BOOL Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes,
|
||||||
|
const char *vsFile, const char *fsFile);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CRender3D(void):
|
||||||
|
* ~CRender3D(void):
|
||||||
|
*
|
||||||
|
* Constructor and destructor.
|
||||||
|
*/
|
||||||
|
CRender3D(void);
|
||||||
|
~CRender3D(void);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/*
|
||||||
|
* Private Members
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Real3D address translation
|
||||||
|
const UINT32 *TranslateCullingAddress(UINT32 addr);
|
||||||
|
const UINT32 *TranslateModelAddress(UINT32 addr);
|
||||||
|
|
||||||
|
// Model caching and display list management
|
||||||
|
void DrawDisplayList(ModelCache *Cache, POLY_STATE state);
|
||||||
|
BOOL AppendDisplayList(ModelCache *Cache, BOOL isViewport, int modelNum);
|
||||||
|
void ClearDisplayList(ModelCache *Cache);
|
||||||
|
BOOL InsertPolygon(ModelCache *cache, const Poly *p);
|
||||||
|
void InsertVertex(ModelCache *cache, const Vertex *v, const Poly *p, float normFlip);
|
||||||
|
BOOL BeginModel(ModelCache *cache);
|
||||||
|
void EndModel(ModelCache *cache, int lutIdx);
|
||||||
|
BOOL CacheModel(ModelCache *cache, int lutIdx, const UINT32 *data);
|
||||||
|
BOOL NeedToCache(ModelCache *cache, int lutIdx);
|
||||||
|
void ClearModelCache(ModelCache *cache);
|
||||||
|
BOOL CreateModelCache(ModelCache *cache, unsigned vboMaxVerts, unsigned localMaxVerts, unsigned maxNumModels, unsigned numLUTEntries, unsigned displayListSize, BOOL isDynamic);
|
||||||
|
void DestroyModelCache(ModelCache *cache);
|
||||||
|
|
||||||
|
// Texture management
|
||||||
|
void DecodeTexture(int format, int x, int y, int width, int height);
|
||||||
|
|
||||||
|
// Stack management
|
||||||
|
void MultMatrix(UINT32 matrixOffset);
|
||||||
|
void InitMatrixStack(UINT32 matrixBaseAddr);
|
||||||
|
void Push(UINT32 ptr, BOOL pushMatrix);
|
||||||
|
UINT32 Pop(void);
|
||||||
|
void ClearStack(void);
|
||||||
|
|
||||||
|
// Scene database traversal
|
||||||
|
BOOL DrawModel(UINT32 modelAddr);
|
||||||
|
void DescendCullingNode(UINT32 addr);
|
||||||
|
void DescendPointerList(UINT32 addr);
|
||||||
|
void DescendNodePtr(UINT32 nodeAddr);
|
||||||
|
void StackMachine(UINT32 nodeAddr);
|
||||||
|
void RenderViewport(UINT32 addr, int pri);
|
||||||
|
|
||||||
|
// In-frame error reporting
|
||||||
|
BOOL ErrorLocalVertexOverflow(void);
|
||||||
|
BOOL ErrorUnableToCacheModel(UINT32 modelAddr);
|
||||||
|
void ClearErrors(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Data
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Stepping
|
||||||
|
int step;
|
||||||
|
int offset; // offset to subtract for words 3 and higher of culling nodes
|
||||||
|
GLfloat vertexFactor; // fixed-point conversion factor for vertices
|
||||||
|
|
||||||
|
// Memory (passed from outside)
|
||||||
|
const UINT32 *cullingRAMLo; // 4 MB
|
||||||
|
const UINT32 *cullingRAMHi; // 1 MB
|
||||||
|
const UINT32 *polyRAM; // 4 MB
|
||||||
|
const UINT32 *vrom; // 64 MB
|
||||||
|
const UINT16 *textureRAM; // 8 MB
|
||||||
|
|
||||||
|
// Error reporting
|
||||||
|
unsigned errorMsgFlags; // tracks which errors have been printed this frame
|
||||||
|
|
||||||
|
// Real3D Base Matrix Pointer
|
||||||
|
const float *matrixBasePtr;
|
||||||
|
|
||||||
|
// Processing stack (experimental)
|
||||||
|
UINT32 *stack;
|
||||||
|
int stackSize; // number of elements stack can contain
|
||||||
|
int stackTop; // current top of stack (free spot, last element is stackTop-1)
|
||||||
|
BOOL stackOverflow; // records stack overflows (cleared by ClearStack())
|
||||||
|
|
||||||
|
// Current viewport parameters (updated as viewports are traversed)
|
||||||
|
GLfloat lightingParams[6];
|
||||||
|
GLfloat fogParams[5];
|
||||||
|
GLfloat spotEllipse[4];
|
||||||
|
GLfloat spotRange[2];
|
||||||
|
GLfloat spotColor[3];
|
||||||
|
GLint viewportX, viewportY;
|
||||||
|
GLint viewportWidth, viewportHeight;
|
||||||
|
|
||||||
|
// Scene graph stack
|
||||||
|
int listDepth; // how many lists have we recursed into
|
||||||
|
int stackDepth; // for debugging and error handling purposes
|
||||||
|
|
||||||
|
// Resolution scaling factors (to support resolutions higher than 496x384) and offsets
|
||||||
|
GLfloat xRatio, yRatio;
|
||||||
|
unsigned xOffs, yOffs;
|
||||||
|
|
||||||
|
// Texture ID for complete 2048x2048 texture map
|
||||||
|
GLuint texID;
|
||||||
|
|
||||||
|
// Shader programs and input data locations
|
||||||
|
GLuint shaderProgram; // shader program object
|
||||||
|
GLuint vertexShader; // vertex shader handle
|
||||||
|
GLuint fragmentShader; // fragment shader
|
||||||
|
GLuint textureMapLoc; // location of "textureMap" uniform
|
||||||
|
GLuint modelViewMatrixLoc; // uniform
|
||||||
|
GLuint projectionMatrixLoc; // uniform
|
||||||
|
GLuint lightingLoc; // uniform
|
||||||
|
GLuint spotEllipseLoc; // uniform
|
||||||
|
GLuint spotRangeLoc; // uniform
|
||||||
|
GLuint spotColorLoc; // uniform
|
||||||
|
GLuint subTextureLoc; // attribute
|
||||||
|
GLuint texParamsLoc; // attribute
|
||||||
|
GLuint texFormatLoc; // attribute
|
||||||
|
GLuint transLevelLoc; // attribute
|
||||||
|
GLuint lightEnableLoc; // attribute
|
||||||
|
GLuint fogIntensityLoc; // attribute
|
||||||
|
|
||||||
|
// Model caching
|
||||||
|
ModelCache VROMCache; // VROM (static) models
|
||||||
|
ModelCache PolyCache; // polygon RAM (dynamic) models
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Texture Format Buffer
|
||||||
|
*
|
||||||
|
* Records the format that a texture (at a given location within the
|
||||||
|
* texture sheet) is currently stored in. A negative value indicates the
|
||||||
|
* texture has not been accessed and converted yet and non-negative values
|
||||||
|
* correspond to the texture format bits in the polygon headers. They can
|
||||||
|
* be used to determine whether a texture needs to be updated.
|
||||||
|
*/
|
||||||
|
int textureWidth[2048/32][2048/32];
|
||||||
|
int textureHeight[2048/32][2048/32];
|
||||||
|
INT8 textureFormat[2048/32][2048/32];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Texture Decode Buffer
|
||||||
|
*
|
||||||
|
* Textures are decoded and copied from texture RAM into this temporary buffer
|
||||||
|
* before being uploaded. Dimensions are 512x512.
|
||||||
|
*/
|
||||||
|
GLfloat *textureBuffer; // RGBA8 format
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // INCLUDED_RENDER3D_H
|
182
Graphics/Shader.cpp
Normal file
182
Graphics/Shader.cpp
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Shader.cpp
|
||||||
|
*
|
||||||
|
* OpenGL shader management.
|
||||||
|
*
|
||||||
|
* To-Do List
|
||||||
|
* ----------
|
||||||
|
* - Mesa crashes because, evidently, the function pointers are invalid. Mesa
|
||||||
|
* returns the following information:
|
||||||
|
* Vendor: Mesa project: www.mesa3d.org
|
||||||
|
* Renderer: Mesa GLX Indirect
|
||||||
|
* Version: 1.2 (1.5 Mesa 6.5.1)
|
||||||
|
* Shading Language Version: (null)
|
||||||
|
* Maximum Vertex Array Size: -1 vertices
|
||||||
|
* Maximum Texture Size: 2048 texels
|
||||||
|
* Maximum Vertex Attributes: 16
|
||||||
|
* Maximum Vertex Uniforms: 16
|
||||||
|
* - Check for OpenGL 2.0 and perhaps check some of the function pointers,
|
||||||
|
* which will be NULL, if GL 2.0 and shaders are not supported.
|
||||||
|
* - Keep in mind that all these checks should probably go somewhere else...
|
||||||
|
* - Turn this into a class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <new>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "Pkgs/glew.h"
|
||||||
|
#include "Supermodel.h"
|
||||||
|
|
||||||
|
|
||||||
|
// Load a source file. Pointer returned must be freed by caller. Returns NULL if failed.
|
||||||
|
static char *LoadShaderSource(const char *file)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
char *buf;
|
||||||
|
int size;
|
||||||
|
|
||||||
|
// Open shader and get the file size
|
||||||
|
fp = fopen(file, "r");
|
||||||
|
if (NULL == fp)
|
||||||
|
{
|
||||||
|
ErrorLog("Unable to open shader source file: %s", file);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
fseek(fp, 0, SEEK_END);
|
||||||
|
size = ftell(fp);
|
||||||
|
rewind(fp);
|
||||||
|
|
||||||
|
// Allocate memory and read it in
|
||||||
|
buf = new(std::nothrow) char[size+1];
|
||||||
|
if (NULL == buf)
|
||||||
|
{
|
||||||
|
ErrorLog("Insufficient memory to load shader source file: %s", file);
|
||||||
|
fclose(fp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
buf[size] = '\0'; // for safety, actual size might be smaller once newline characters are converted
|
||||||
|
size = fread(buf, sizeof(char), size, fp);
|
||||||
|
buf[size] = '\0';
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL LoadShaderProgram(GLuint *shaderProgramPtr, GLuint *vertexShaderPtr, GLuint *fragmentShaderPtr, const char *vsFile, const char *fsFile, const char *vsString, const char *fsString)
|
||||||
|
{
|
||||||
|
char msg[2048+128], infoLog[2048];
|
||||||
|
const char *vsSource, *fsSource; // source code
|
||||||
|
GLuint shaderProgram, vertexShader, fragmentShader;
|
||||||
|
GLint result, len;
|
||||||
|
BOOL ret = OKAY;
|
||||||
|
|
||||||
|
// Load shaders from files if specified
|
||||||
|
if (vsFile != NULL)
|
||||||
|
vsSource = LoadShaderSource(vsFile);
|
||||||
|
else
|
||||||
|
vsSource = vsString;
|
||||||
|
if (fsFile != NULL)
|
||||||
|
fsSource = LoadShaderSource(fsFile);
|
||||||
|
else
|
||||||
|
fsSource = fsString;
|
||||||
|
if (vsSource == NULL || fsSource == NULL)
|
||||||
|
{
|
||||||
|
ret = FAIL;
|
||||||
|
goto Quit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that shader support exists
|
||||||
|
if ((glCreateProgram==NULL) || (glCreateShader==NULL) || (glShaderSource==NULL) || (glCompileShader==NULL))
|
||||||
|
{
|
||||||
|
ret = FAIL;
|
||||||
|
ErrorLog("OpenGL 2.0 does not appear to be present. Unable to proceed.");
|
||||||
|
goto Quit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the shaders and shader program
|
||||||
|
shaderProgram = glCreateProgram();
|
||||||
|
vertexShader = glCreateShader(GL_VERTEX_SHADER);
|
||||||
|
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||||
|
*shaderProgramPtr = shaderProgram;
|
||||||
|
*vertexShaderPtr = vertexShader;
|
||||||
|
*fragmentShaderPtr = fragmentShader;
|
||||||
|
|
||||||
|
// Attempt to compile vertex shader
|
||||||
|
glShaderSource(vertexShader, 1, (const GLchar **) &vsSource, NULL);
|
||||||
|
glCompileShader(vertexShader);
|
||||||
|
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &result);
|
||||||
|
if (!result) // failed to compile
|
||||||
|
{
|
||||||
|
glGetShaderInfoLog(vertexShader, 2048, &len, infoLog);
|
||||||
|
ErrorLog("Vertex shader failed to compile. Compiler says:\n%s", infoLog);
|
||||||
|
ret = FAIL; // error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to compile fragment shader
|
||||||
|
glShaderSource(fragmentShader, 1, (const GLchar **) &fsSource, NULL);
|
||||||
|
glCompileShader(fragmentShader);
|
||||||
|
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &result);
|
||||||
|
if (!result) // failed to compile
|
||||||
|
{
|
||||||
|
glGetShaderInfoLog(fragmentShader, 2048, &len, infoLog);
|
||||||
|
ErrorLog("Fragment shader failed to compile. Compiler says:\n%s", infoLog);
|
||||||
|
fprintf(stderr, msg);
|
||||||
|
ret = FAIL; // error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Link
|
||||||
|
glAttachShader(shaderProgram, vertexShader);
|
||||||
|
glAttachShader(shaderProgram, fragmentShader);
|
||||||
|
glLinkProgram(shaderProgram);
|
||||||
|
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &result);
|
||||||
|
if (result == GL_FALSE)
|
||||||
|
{
|
||||||
|
glGetProgramInfoLog(shaderProgram, 2048, &len, infoLog);
|
||||||
|
ErrorLog("Failed to link shader objects. Linker says:\n%s\n", infoLog);
|
||||||
|
ret = FAIL; // error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable the shader (if no errors)
|
||||||
|
if (ret == OKAY)
|
||||||
|
glUseProgram(shaderProgram);
|
||||||
|
|
||||||
|
// Clean up and quit
|
||||||
|
Quit:
|
||||||
|
if ((vsSource != NULL) && (vsFile != NULL)) // loaded from file, must delete
|
||||||
|
delete [] vsSource;
|
||||||
|
if ((fsSource != NULL) && (fsFile != NULL)) // ""
|
||||||
|
delete [] fsSource;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DestroyShaderProgram(GLuint shaderProgram, GLuint vertexShader, GLuint fragmentShader)
|
||||||
|
{
|
||||||
|
// In case LoadShaderProgram() failed above due to lack of OpenGL 2.0+ functions...
|
||||||
|
if ((glUseProgram==NULL) || (glDeleteShader==NULL) || (glDeleteProgram==NULL))
|
||||||
|
return;
|
||||||
|
|
||||||
|
glUseProgram(0); // return to fixed function pipeline
|
||||||
|
glDeleteShader(vertexShader);
|
||||||
|
glDeleteShader(fragmentShader);
|
||||||
|
glDeleteProgram(shaderProgram);
|
||||||
|
}
|
73
Graphics/Shader.h
Normal file
73
Graphics/Shader.h
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Shader.h
|
||||||
|
*
|
||||||
|
* OpenGL shader management functions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDED_SHADER_H
|
||||||
|
#define INCLUDED_SHADER_H
|
||||||
|
|
||||||
|
#include "Pkgs/glew.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LoadShaderProgram(shaderProgramPtr, vertexShaderPtr, fragmentShaderPtr,
|
||||||
|
* vsFile, fsFile, vsString, fsString):
|
||||||
|
*
|
||||||
|
* Loads and creates an OpenGL shader program.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* shaderProgramPtr Pointer to where OpenGL shader program handle is
|
||||||
|
* written.
|
||||||
|
* vertexShaderPtr Pointer to where vertex shader handle is written.
|
||||||
|
* fragmentShaderPtr Pointer for fragment shader.
|
||||||
|
* vsFile Vertex shader file path. If this is not NULL, the
|
||||||
|
* vertex shader is loaded from the file.
|
||||||
|
* fsFile Fragment shader file path. If this is not NULL, the
|
||||||
|
* fragment shader is loaded from the file.
|
||||||
|
* vsString String containing the vertex shader.
|
||||||
|
* fsString String containing the fragment shader.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* OKAY is successfully loaded, otherwise FAIL. Prints own error messages.
|
||||||
|
*/
|
||||||
|
extern BOOL LoadShaderProgram(GLuint *shaderProgramPtr, GLuint *vertexShaderPtr,
|
||||||
|
GLuint *fragmentShaderPtr, const char *vsFile,
|
||||||
|
const char *fsFile, const char *vsString,
|
||||||
|
const char *fsString);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DestroyShaderProgram(shaderProgram, vertexShader, fragmentShader):
|
||||||
|
*
|
||||||
|
* Removes shaders and returns to the fixed function OpenGL pipeline.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* shaderProgram Handle for shader program.
|
||||||
|
* vertexShader Handle for vertex shader.
|
||||||
|
* fragmentShader Handle for fragment shader.
|
||||||
|
*/
|
||||||
|
extern void DestroyShaderProgram(GLuint shaderProgram, GLuint vertexShader,
|
||||||
|
GLuint fragmentShader);
|
||||||
|
|
||||||
|
|
||||||
|
#endif // INCLUDED_SHADER_H
|
2
Graphics/Shaders/DIR.txt
Normal file
2
Graphics/Shaders/DIR.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Shader source files go here. Completed versions of the shader files should be
|
||||||
|
copied into Src/Graphics/Shaders3D.h and Src/Graphics/Shaders2D.h.
|
194
Graphics/Shaders/Fragment.glsl
Normal file
194
Graphics/Shaders/Fragment.glsl
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fragment.glsl
|
||||||
|
*
|
||||||
|
* Fragment shader for 3D rendering.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#version 120
|
||||||
|
|
||||||
|
// Global uniforms
|
||||||
|
uniform sampler2D textureMap; // complete texture map, 2048x2048 texels
|
||||||
|
uniform vec4 spotEllipse; // spotlight ellipse position: .x=X position (screen coordinates), .y=Y position, .z=half-width, .w=half-height)
|
||||||
|
uniform vec2 spotRange; // spotlight Z range: .x=start (viewspace coordinates), .y=limit
|
||||||
|
uniform vec3 spotColor; // spotlight RGB color
|
||||||
|
|
||||||
|
// Inputs from vertex shader
|
||||||
|
varying vec4 fsSubTexture; // .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels)
|
||||||
|
varying vec4 fsTexParams; // .x=texture enable (if 1, else 0), .y=use transparency (if > 0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode
|
||||||
|
varying float fsTexFormat; // .x=T1RGB5 contour texture (if > 0)
|
||||||
|
varying float fsTransLevel; // translucence level, 0.0 (transparent) to 1.0 (opaque)
|
||||||
|
varying vec3 fsLightIntensity; // lighting intensity
|
||||||
|
varying float fsFogFactor; // fog factor
|
||||||
|
varying float fsViewZ; // Z distance to fragment from viewpoint at origin
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WrapTexelCoords():
|
||||||
|
*
|
||||||
|
* Computes the normalized OpenGL S,T coordinates within the 2048x2048 texture
|
||||||
|
* sheet, taking into account wrapping behavior.
|
||||||
|
*
|
||||||
|
* Computing normalized OpenGL texture coordinates (0 to 1) within the
|
||||||
|
* Real3D texture sheet:
|
||||||
|
*
|
||||||
|
* If the texture is not mirrored, we simply have to clamp the
|
||||||
|
* coordinates to fit within the texture dimensions, add the texture
|
||||||
|
* X, Y position to select the appropriate one, and normalize by 2048
|
||||||
|
* (the dimensions of the Real3D texture sheet).
|
||||||
|
*
|
||||||
|
* = [(u,v)%(w,h)+(x,y)]/(2048,2048)
|
||||||
|
*
|
||||||
|
* If mirroring is enabled, textures are mirrored every odd multiple of
|
||||||
|
* the original texture. To detect whether we are in an odd multiple,
|
||||||
|
* simply divide the coordinate by the texture dimension and check
|
||||||
|
* whether the result is odd. Then, clamp the coordinates as before but
|
||||||
|
* subtract from the last texel to mirror them:
|
||||||
|
*
|
||||||
|
* = [M*((w-1,h-1)-(u,v)%(w,h)) + (1-M)*(u,v)%(w,h) + (x,y)]/(2048,2048)
|
||||||
|
* where M is 1.0 if the texture must be mirrored.
|
||||||
|
*
|
||||||
|
* As an optimization, this function computes TWO texture coordinates
|
||||||
|
* simultaneously. The first is texCoord.xy, the second is in .zw. The other
|
||||||
|
* parameters must have .xy = .zw.
|
||||||
|
*/
|
||||||
|
vec4 WrapTexelCoords(vec4 texCoord, vec4 texOffset, vec4 texSize, vec4 mirrorEnable)
|
||||||
|
{
|
||||||
|
vec4 clampedCoord, mirror, glTexCoord;
|
||||||
|
|
||||||
|
clampedCoord = mod(texCoord,texSize); // clamp coordinates to within texture size
|
||||||
|
mirror = mirrorEnable * mod(floor(texCoord/texSize),2.0); // whether this texel needs to be mirrored
|
||||||
|
|
||||||
|
glTexCoord = ( mirror*(texSize-clampedCoord) +
|
||||||
|
(vec4(1.0,1.0,1.0,1.0)-mirror)*clampedCoord +
|
||||||
|
texOffset
|
||||||
|
) / 2048.0;
|
||||||
|
/*
|
||||||
|
glTexCoord = ( mirror*(texSize-vec4(1.0,1.0,1.0,1.0)-clampedCoord) +
|
||||||
|
(vec4(1.0,1.0,1.0,1.0)-mirror)*clampedCoord +
|
||||||
|
texOffset
|
||||||
|
) / 2048.0;
|
||||||
|
*/
|
||||||
|
return glTexCoord;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* main():
|
||||||
|
*
|
||||||
|
* Fragment shader entry point.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
vec4 uv_top, uv_bot, c[4];
|
||||||
|
vec2 r;
|
||||||
|
vec4 fragColor;
|
||||||
|
vec2 ellipse;
|
||||||
|
vec3 lightIntensity;
|
||||||
|
float insideSpot;
|
||||||
|
|
||||||
|
// Get polygon color for untextured polygons (textured polygons will overwrite)
|
||||||
|
if (fsTexParams.x==0.0)
|
||||||
|
fragColor = gl_Color;
|
||||||
|
else
|
||||||
|
// Textured polygons: set fragment color to texel value
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Bilinear Filtering
|
||||||
|
*
|
||||||
|
* In order to get this working on ATI, the number of operations is
|
||||||
|
* reduced by putting everything into vec4s. uv_top holds the UV
|
||||||
|
* coordinates for the top two texels (.xy=left, .zw=right) and uv_bot
|
||||||
|
* is for the lower two.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Compute fractional blending factor, r, and lower left corner of texel 0
|
||||||
|
uv_bot.xy = gl_TexCoord[0].st-vec2(0.5,0.5); // move into the lower left blending texel
|
||||||
|
r = uv_bot.xy-floor(uv_bot.xy); // fractional part
|
||||||
|
uv_bot.xy = floor(uv_bot.xy); // integral part
|
||||||
|
|
||||||
|
// Compute texel coordinates
|
||||||
|
uv_bot.xy += vec2(0.5,0.5); // offset to center of pixel (should not be needed but it fixes a lot of glitches, esp. on Nvidia)
|
||||||
|
uv_bot.zw = uv_bot.xy + vec2(1.0,0.0); // compute coordinates of the other three neighbors
|
||||||
|
uv_top = uv_bot + vec4(0.0,1.0,0.0,1.0);
|
||||||
|
|
||||||
|
// Compute the properly wrapped texel coordinates
|
||||||
|
uv_top = WrapTexelCoords(uv_top,vec4(fsSubTexture.xy,fsSubTexture.xy),vec4(fsSubTexture.zw,fsSubTexture.zw), vec4(fsTexParams.zw,fsTexParams.zw));
|
||||||
|
uv_bot = WrapTexelCoords(uv_bot,vec4(fsSubTexture.xy,fsSubTexture.xy),vec4(fsSubTexture.zw,fsSubTexture.zw), vec4(fsTexParams.zw,fsTexParams.zw));
|
||||||
|
|
||||||
|
// Fetch the texels
|
||||||
|
c[0]=texture2D(textureMap,uv_bot.xy); // bottom-left (base texel)
|
||||||
|
c[1]=texture2D(textureMap,uv_bot.zw); // bottom-right
|
||||||
|
c[2]=texture2D(textureMap,uv_top.xy); // top-left
|
||||||
|
c[3]=texture2D(textureMap,uv_top.zw); // top-right
|
||||||
|
|
||||||
|
// Interpolate texels and blend result with material color to determine final (unlit) fragment color
|
||||||
|
// fragColor = (c[0]*(1.0-r.s)*(1.0-r.t) + c[1]*r.s*(1.0-r.t) + c[2]*(1.0-r.s)*r.t + c[3]*r.s*r.t);
|
||||||
|
// Faster method:
|
||||||
|
c[0] += (c[1]-c[0])*r.s; // 2 alu
|
||||||
|
c[2] += (c[3]-c[2])*r.s; // 2 alu
|
||||||
|
fragColor = c[0]+(c[2]-c[0])*r.t; //2 alu
|
||||||
|
|
||||||
|
/*
|
||||||
|
* T1RGB5:
|
||||||
|
*
|
||||||
|
* The transparency bit determines whether to discard pixels (if set).
|
||||||
|
* What is unknown is how this bit behaves when interpolated. OpenGL
|
||||||
|
* processes it as an alpha value, so it might concievably be blended
|
||||||
|
* with neighbors. Here, an arbitrary threshold is chosen.
|
||||||
|
*
|
||||||
|
* To-do: blending could probably enabled and this would work even
|
||||||
|
* better with a hard threshold.
|
||||||
|
*
|
||||||
|
* Countour processing also seems to be enabled for RGBA4 textures.
|
||||||
|
* When the alpha value is 0.0 (or close), pixels are discarded
|
||||||
|
* entirely.
|
||||||
|
*/
|
||||||
|
if (fsTexParams.y > 0.0) // contour processing enabled
|
||||||
|
{
|
||||||
|
if (fragColor.a < 0.01) // discard anything with alpha == 0
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If contour texture and not discarded, force alpha to 1.0 because will later be modified by polygon translucency
|
||||||
|
if (fsTexFormat > 0.0) // contour (T1RGB5) texture map
|
||||||
|
fragColor.a = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute spotlight and apply lighting
|
||||||
|
ellipse = (gl_FragCoord.xy-spotEllipse.xy)/spotEllipse.zw;
|
||||||
|
insideSpot = dot(ellipse,ellipse);
|
||||||
|
if ((insideSpot <= 1.0) && (fsViewZ>=spotRange.x) && (fsViewZ<spotRange.y))
|
||||||
|
lightIntensity = min(fsLightIntensity+(1.0-insideSpot)*spotColor,1.0);
|
||||||
|
else
|
||||||
|
lightIntensity = fsLightIntensity;
|
||||||
|
fragColor.rgb *= lightIntensity;
|
||||||
|
|
||||||
|
// Translucency (modulates existing alpha channel for RGBA4 texels)
|
||||||
|
fragColor.a *= fsTransLevel;
|
||||||
|
|
||||||
|
// Apply fog under the control of fog factor setting from polygon header
|
||||||
|
fragColor.rgb = mix(gl_Fog.color.rgb, fragColor.rgb, fsFogFactor );
|
||||||
|
|
||||||
|
// Store final color
|
||||||
|
gl_FragColor = fragColor;
|
||||||
|
}
|
44
Graphics/Shaders/Fragment2D.glsl
Normal file
44
Graphics/Shaders/Fragment2D.glsl
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fragment2D.glsl
|
||||||
|
*
|
||||||
|
* Fragment shader for 2D tilemap rendering.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#version 120
|
||||||
|
|
||||||
|
// Global uniforms
|
||||||
|
uniform sampler2D textureMap; // 512x512 layer surface
|
||||||
|
uniform vec3 colorOffset; // color offset for this layer
|
||||||
|
|
||||||
|
/*
|
||||||
|
* main():
|
||||||
|
*
|
||||||
|
* Fragment shader entry point.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
gl_FragColor = texture2D(textureMap, gl_TexCoord[0].st);
|
||||||
|
gl_FragColor.rgb = clamp(gl_FragColor.rgb+colorOffset,0.0,1.0);
|
||||||
|
}
|
142
Graphics/Shaders/Fragment_Flat.glsl
Normal file
142
Graphics/Shaders/Fragment_Flat.glsl
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fragment_NoSpotlight.glsl
|
||||||
|
*
|
||||||
|
* Fragment shader for 3D rendering. Spotlight effect removed. Fixes fragment
|
||||||
|
* shader link errors on older ATI Radeon GPUs.
|
||||||
|
*
|
||||||
|
* To load external fragment shaders, use the -frag-shader=<file> option when
|
||||||
|
* starting Supermodel.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#version 120
|
||||||
|
|
||||||
|
// Global uniforms
|
||||||
|
uniform sampler2D textureMap; // complete texture map, 2048x2048 texels
|
||||||
|
uniform vec4 spotEllipse; // spotlight ellipse position: .x=X position (screen coordinates), .y=Y position, .z=half-width, .w=half-height)
|
||||||
|
uniform vec2 spotRange; // spotlight Z range: .x=start (viewspace coordinates), .y=limit
|
||||||
|
uniform vec3 spotColor; // spotlight RGB color
|
||||||
|
|
||||||
|
// Inputs from vertex shader
|
||||||
|
varying vec4 fsSubTexture; // .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels)
|
||||||
|
varying vec4 fsTexParams; // .x=texture enable (if 1, else 0), .y=use transparency (if > 0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode
|
||||||
|
varying float fsTexFormat; // .x=T1RGB5 contour texture (if > 0)
|
||||||
|
varying float fsTransLevel; // translucence level, 0.0 (transparent) to 1.0 (opaque)
|
||||||
|
varying vec3 fsLightIntensity; // lighting intensity
|
||||||
|
varying float fsFogFactor; // fog factor
|
||||||
|
varying float fsViewZ; // Z distance to fragment from viewpoint at origin
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WrapTexelCoords():
|
||||||
|
*
|
||||||
|
* Computes the normalized OpenGL S,T coordinates within the 2048x2048 texture
|
||||||
|
* sheet, taking into account wrapping behavior.
|
||||||
|
*
|
||||||
|
* Computing normalized OpenGL texture coordinates (0 to 1) within the
|
||||||
|
* Real3D texture sheet:
|
||||||
|
*
|
||||||
|
* If the texture is not mirrored, we simply have to clamp the
|
||||||
|
* coordinates to fit within the texture dimensions, add the texture
|
||||||
|
* X, Y position to select the appropriate one, and normalize by 2048
|
||||||
|
* (the dimensions of the Real3D texture sheet).
|
||||||
|
*
|
||||||
|
* = [(u,v)%(w,h)+(x,y)]/(2048,2048)
|
||||||
|
*
|
||||||
|
* If mirroring is enabled, textures are mirrored every odd multiple of
|
||||||
|
* the original texture. To detect whether we are in an odd multiple,
|
||||||
|
* simply divide the coordinate by the texture dimension and check
|
||||||
|
* whether the result is odd. Then, clamp the coordinates as before but
|
||||||
|
* subtract from the last texel to mirror them:
|
||||||
|
*
|
||||||
|
* = [M*((w-1,h-1)-(u,v)%(w,h)) + (1-M)*(u,v)%(w,h) + (x,y)]/(2048,2048)
|
||||||
|
* where M is 1.0 if the texture must be mirrored.
|
||||||
|
*
|
||||||
|
* As an optimization, this function computes TWO texture coordinates
|
||||||
|
* simultaneously. The first is texCoord.xy, the second is in .zw. The other
|
||||||
|
* parameters must have .xy = .zw.
|
||||||
|
*/
|
||||||
|
vec4 WrapTexelCoords(vec4 texCoord, vec4 texOffset, vec4 texSize, vec4 mirrorEnable)
|
||||||
|
{
|
||||||
|
vec4 clampedCoord, mirror, glTexCoord;
|
||||||
|
|
||||||
|
clampedCoord = mod(texCoord,texSize); // clamp coordinates to within texture size
|
||||||
|
mirror = mirrorEnable * mod(floor(texCoord/texSize),2.0); // whether this texel needs to be mirrored
|
||||||
|
|
||||||
|
glTexCoord = ( mirror*(texSize-clampedCoord) +
|
||||||
|
(vec4(1.0,1.0,1.0,1.0)-mirror)*clampedCoord +
|
||||||
|
texOffset
|
||||||
|
) / 2048.0;
|
||||||
|
/*
|
||||||
|
glTexCoord = ( mirror*(texSize-vec4(1.0,1.0,1.0,1.0)-clampedCoord) +
|
||||||
|
(vec4(1.0,1.0,1.0,1.0)-mirror)*clampedCoord +
|
||||||
|
texOffset
|
||||||
|
) / 2048.0;
|
||||||
|
*/
|
||||||
|
return glTexCoord;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* main():
|
||||||
|
*
|
||||||
|
* Fragment shader entry point.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
vec4 uv_top, uv_bot, c[4];
|
||||||
|
vec2 r;
|
||||||
|
vec4 fragColor;
|
||||||
|
vec2 ellipse;
|
||||||
|
vec3 lightIntensity;
|
||||||
|
float insideSpot;
|
||||||
|
|
||||||
|
// Get polygon color for untextured polygons (textured polygons will overwrite)
|
||||||
|
if (fsTexParams.x==0.0)
|
||||||
|
fragColor = gl_Color;
|
||||||
|
else
|
||||||
|
// Textured polygons: set fragment color to texel value
|
||||||
|
{
|
||||||
|
fragColor = texture2D(textureMap,(fsSubTexture.xy+fsSubTexture.zw/2.0)/2048.0);
|
||||||
|
//fragColor += texture2D(textureMap,(fsSubTexture.xy+fsSubTexture.zw))/2048.0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute spotlight and apply lighting
|
||||||
|
ellipse = (gl_FragCoord.xy-spotEllipse.xy)/spotEllipse.zw;
|
||||||
|
insideSpot = dot(ellipse,ellipse);
|
||||||
|
if ((insideSpot <= 1.0) && (fsViewZ>=spotRange.x) && (fsViewZ<spotRange.y))
|
||||||
|
lightIntensity = min(fsLightIntensity+(1.0-insideSpot)*spotColor,1.0);
|
||||||
|
else
|
||||||
|
lightIntensity = fsLightIntensity;
|
||||||
|
fragColor.rgb *= lightIntensity;
|
||||||
|
fragColor.rgb *= fsLightIntensity;
|
||||||
|
|
||||||
|
// Translucency (modulates existing alpha channel for RGBA4 texels)
|
||||||
|
fragColor.a *= fsTransLevel;
|
||||||
|
|
||||||
|
// Apply fog under the control of fog factor setting from polygon header
|
||||||
|
fragColor.rgb = mix(gl_Fog.color.rgb, fragColor.rgb, fsFogFactor );
|
||||||
|
|
||||||
|
// Store final color
|
||||||
|
gl_FragColor = fragColor;
|
||||||
|
}
|
201
Graphics/Shaders/Fragment_NoSpotlight.glsl
Normal file
201
Graphics/Shaders/Fragment_NoSpotlight.glsl
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fragment_NoSpotlight.glsl
|
||||||
|
*
|
||||||
|
* Fragment shader for 3D rendering. Spotlight effect removed. Fixes fragment
|
||||||
|
* shader link errors on older ATI Radeon GPUs.
|
||||||
|
*
|
||||||
|
* To load external fragment shaders, use the -frag-shader=<file> option when
|
||||||
|
* starting Supermodel.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#version 120
|
||||||
|
|
||||||
|
// Global uniforms
|
||||||
|
uniform sampler2D textureMap; // complete texture map, 2048x2048 texels
|
||||||
|
uniform vec4 spotEllipse; // spotlight ellipse position: .x=X position (screen coordinates), .y=Y position, .z=half-width, .w=half-height)
|
||||||
|
uniform vec2 spotRange; // spotlight Z range: .x=start (viewspace coordinates), .y=limit
|
||||||
|
uniform vec3 spotColor; // spotlight RGB color
|
||||||
|
|
||||||
|
// Inputs from vertex shader
|
||||||
|
varying vec4 fsSubTexture; // .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels)
|
||||||
|
varying vec4 fsTexParams; // .x=texture enable (if 1, else 0), .y=use transparency (if > 0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode
|
||||||
|
varying float fsTexFormat; // .x=T1RGB5 contour texture (if > 0)
|
||||||
|
varying float fsTransLevel; // translucence level, 0.0 (transparent) to 1.0 (opaque)
|
||||||
|
varying vec3 fsLightIntensity; // lighting intensity
|
||||||
|
varying float fsFogFactor; // fog factor
|
||||||
|
varying float fsViewZ; // Z distance to fragment from viewpoint at origin
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WrapTexelCoords():
|
||||||
|
*
|
||||||
|
* Computes the normalized OpenGL S,T coordinates within the 2048x2048 texture
|
||||||
|
* sheet, taking into account wrapping behavior.
|
||||||
|
*
|
||||||
|
* Computing normalized OpenGL texture coordinates (0 to 1) within the
|
||||||
|
* Real3D texture sheet:
|
||||||
|
*
|
||||||
|
* If the texture is not mirrored, we simply have to clamp the
|
||||||
|
* coordinates to fit within the texture dimensions, add the texture
|
||||||
|
* X, Y position to select the appropriate one, and normalize by 2048
|
||||||
|
* (the dimensions of the Real3D texture sheet).
|
||||||
|
*
|
||||||
|
* = [(u,v)%(w,h)+(x,y)]/(2048,2048)
|
||||||
|
*
|
||||||
|
* If mirroring is enabled, textures are mirrored every odd multiple of
|
||||||
|
* the original texture. To detect whether we are in an odd multiple,
|
||||||
|
* simply divide the coordinate by the texture dimension and check
|
||||||
|
* whether the result is odd. Then, clamp the coordinates as before but
|
||||||
|
* subtract from the last texel to mirror them:
|
||||||
|
*
|
||||||
|
* = [M*((w-1,h-1)-(u,v)%(w,h)) + (1-M)*(u,v)%(w,h) + (x,y)]/(2048,2048)
|
||||||
|
* where M is 1.0 if the texture must be mirrored.
|
||||||
|
*
|
||||||
|
* As an optimization, this function computes TWO texture coordinates
|
||||||
|
* simultaneously. The first is texCoord.xy, the second is in .zw. The other
|
||||||
|
* parameters must have .xy = .zw.
|
||||||
|
*/
|
||||||
|
vec4 WrapTexelCoords(vec4 texCoord, vec4 texOffset, vec4 texSize, vec4 mirrorEnable)
|
||||||
|
{
|
||||||
|
vec4 clampedCoord, mirror, glTexCoord;
|
||||||
|
|
||||||
|
clampedCoord = mod(texCoord,texSize); // clamp coordinates to within texture size
|
||||||
|
mirror = mirrorEnable * mod(floor(texCoord/texSize),2.0); // whether this texel needs to be mirrored
|
||||||
|
|
||||||
|
glTexCoord = ( mirror*(texSize-clampedCoord) +
|
||||||
|
(vec4(1.0,1.0,1.0,1.0)-mirror)*clampedCoord +
|
||||||
|
texOffset
|
||||||
|
) / 2048.0;
|
||||||
|
/*
|
||||||
|
glTexCoord = ( mirror*(texSize-vec4(1.0,1.0,1.0,1.0)-clampedCoord) +
|
||||||
|
(vec4(1.0,1.0,1.0,1.0)-mirror)*clampedCoord +
|
||||||
|
texOffset
|
||||||
|
) / 2048.0;
|
||||||
|
*/
|
||||||
|
return glTexCoord;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* main():
|
||||||
|
*
|
||||||
|
* Fragment shader entry point.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
vec4 uv_top, uv_bot, c[4];
|
||||||
|
vec2 r;
|
||||||
|
vec4 fragColor;
|
||||||
|
vec2 ellipse;
|
||||||
|
vec3 lightIntensity;
|
||||||
|
float insideSpot;
|
||||||
|
|
||||||
|
// Get polygon color for untextured polygons (textured polygons will overwrite)
|
||||||
|
if (fsTexParams.x==0.0)
|
||||||
|
fragColor = gl_Color;
|
||||||
|
else
|
||||||
|
// Textured polygons: set fragment color to texel value
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Bilinear Filtering
|
||||||
|
*
|
||||||
|
* In order to get this working on ATI, the number of operations is
|
||||||
|
* reduced by putting everything into vec4s. uv_top holds the UV
|
||||||
|
* coordinates for the top two texels (.xy=left, .zw=right) and uv_bot
|
||||||
|
* is for the lower two.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Compute fractional blending factor, r, and lower left corner of texel 0
|
||||||
|
uv_bot.xy = gl_TexCoord[0].st-vec2(0.5,0.5); // move into the lower left blending texel
|
||||||
|
r = uv_bot.xy-floor(uv_bot.xy); // fractional part
|
||||||
|
uv_bot.xy = floor(uv_bot.xy); // integral part
|
||||||
|
|
||||||
|
// Compute texel coordinates
|
||||||
|
uv_bot.xy += vec2(0.5,0.5); // offset to center of pixel (should not be needed but it fixes a lot of glitches, esp. on Nvidia)
|
||||||
|
uv_bot.zw = uv_bot.xy + vec2(1.0,0.0); // compute coordinates of the other three neighbors
|
||||||
|
uv_top = uv_bot + vec4(0.0,1.0,0.0,1.0);
|
||||||
|
|
||||||
|
// Compute the properly wrapped texel coordinates
|
||||||
|
uv_top = WrapTexelCoords(uv_top,vec4(fsSubTexture.xy,fsSubTexture.xy),vec4(fsSubTexture.zw,fsSubTexture.zw), vec4(fsTexParams.zw,fsTexParams.zw));
|
||||||
|
uv_bot = WrapTexelCoords(uv_bot,vec4(fsSubTexture.xy,fsSubTexture.xy),vec4(fsSubTexture.zw,fsSubTexture.zw), vec4(fsTexParams.zw,fsTexParams.zw));
|
||||||
|
|
||||||
|
// Fetch the texels
|
||||||
|
c[0]=texture2D(textureMap,uv_bot.xy); // bottom-left (base texel)
|
||||||
|
c[1]=texture2D(textureMap,uv_bot.zw); // bottom-right
|
||||||
|
c[2]=texture2D(textureMap,uv_top.xy); // top-left
|
||||||
|
c[3]=texture2D(textureMap,uv_top.zw); // top-right
|
||||||
|
|
||||||
|
// Interpolate texels and blend result with material color to determine final (unlit) fragment color
|
||||||
|
// fragColor = (c[0]*(1.0-r.s)*(1.0-r.t) + c[1]*r.s*(1.0-r.t) + c[2]*(1.0-r.s)*r.t + c[3]*r.s*r.t);
|
||||||
|
// Faster method:
|
||||||
|
c[0] += (c[1]-c[0])*r.s; // 2 alu
|
||||||
|
c[2] += (c[3]-c[2])*r.s; // 2 alu
|
||||||
|
fragColor = c[0]+(c[2]-c[0])*r.t; //2 alu
|
||||||
|
|
||||||
|
/*
|
||||||
|
* T1RGB5:
|
||||||
|
*
|
||||||
|
* The transparency bit determines whether to discard pixels (if set).
|
||||||
|
* What is unknown is how this bit behaves when interpolated. OpenGL
|
||||||
|
* processes it as an alpha value, so it might concievably be blended
|
||||||
|
* with neighbors. Here, an arbitrary threshold is chosen.
|
||||||
|
*
|
||||||
|
* To-do: blending could probably enabled and this would work even
|
||||||
|
* better with a hard threshold.
|
||||||
|
*
|
||||||
|
* Countour processing also seems to be enabled for RGBA4 textures.
|
||||||
|
* When the alpha value is 0.0 (or close), pixels are discarded
|
||||||
|
* entirely.
|
||||||
|
*/
|
||||||
|
if (fsTexParams.y > 0.0) // contour processing enabled
|
||||||
|
{
|
||||||
|
if (fragColor.a < 0.01) // discard anything with alpha == 0
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If contour texture and not discarded, force alpha to 1.0 because will later be modified by polygon translucency
|
||||||
|
if (fsTexFormat > 0.0) // contour (T1RGB5) texture map
|
||||||
|
fragColor.a = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute spotlight and apply lighting
|
||||||
|
/***
|
||||||
|
ellipse = (gl_FragCoord.xy-spotEllipse.xy)/spotEllipse.zw;
|
||||||
|
insideSpot = dot(ellipse,ellipse);
|
||||||
|
if ((insideSpot <= 1.0) && (fsViewZ>=spotRange.x) && (fsViewZ<spotRange.y))
|
||||||
|
lightIntensity = min(fsLightIntensity+(1.0-insideSpot)*spotColor,1.0);
|
||||||
|
else
|
||||||
|
lightIntensity = fsLightIntensity;
|
||||||
|
fragColor.rgb *= lightIntensity;
|
||||||
|
***/
|
||||||
|
fragColor.rgb *= fsLightIntensity;
|
||||||
|
|
||||||
|
// Translucency (modulates existing alpha channel for RGBA4 texels)
|
||||||
|
fragColor.a *= fsTransLevel;
|
||||||
|
|
||||||
|
// Apply fog under the control of fog factor setting from polygon header
|
||||||
|
fragColor.rgb = mix(gl_Fog.color.rgb, fragColor.rgb, fsFogFactor );
|
||||||
|
|
||||||
|
// Store final color
|
||||||
|
gl_FragColor = fragColor;
|
||||||
|
}
|
144
Graphics/Shaders/Vertex.glsl
Normal file
144
Graphics/Shaders/Vertex.glsl
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Vertex.glsl
|
||||||
|
*
|
||||||
|
* Vertex shader for 3D rendering.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#version 120
|
||||||
|
|
||||||
|
// Global uniforms
|
||||||
|
uniform mat4 modelViewMatrix; // model -> view space matrix
|
||||||
|
uniform mat4 projectionMatrix; // view space -> screen space matrix
|
||||||
|
uniform vec3 lighting[2]; // lighting state (lighting[0] = sun direction, lighting[1].x,y = diffuse, ambient intensities from 0-1.0)
|
||||||
|
uniform vec4 spotEllipse; // spotlight ellipse position: .x=X position (normalized device coordinates), .y=Y position, .z=half-width, .w=half-height)
|
||||||
|
uniform vec2 spotRange; // spotlight Z range: .x=start (viewspace coordinates), .y=limit
|
||||||
|
uniform vec3 spotColor; // spotlight RGB color
|
||||||
|
|
||||||
|
// Custom vertex attributes
|
||||||
|
attribute vec4 subTexture; // .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels)
|
||||||
|
attribute vec4 texParams; // .x=texture enable (if 1, else 0), .y=use transparency (if >=0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode
|
||||||
|
attribute float texFormat; // .x=T1RGB5 contour texture (if > 0)
|
||||||
|
attribute float transLevel; // translucence level, 0.0 (transparent) to 1.0 (opaque). if less than 1.0, replace alpha value
|
||||||
|
attribute float lightEnable; // lighting enabled (1.0) or luminous (0.0), drawn at full intensity
|
||||||
|
attribute float fogIntensity; // fog intensity (1.0, full fog effect, 0.0, no fog)
|
||||||
|
|
||||||
|
// Custom outputs to fragment shader
|
||||||
|
varying vec4 fsSubTexture;
|
||||||
|
varying vec4 fsTexParams;
|
||||||
|
varying float fsTexFormat;
|
||||||
|
varying float fsTransLevel;
|
||||||
|
varying vec3 fsLightIntensity; // total light intensity for this vertex
|
||||||
|
varying float fsFogFactor; // fog factor
|
||||||
|
varying float fsViewZ;
|
||||||
|
|
||||||
|
// Gets the 3x3 matrix out of a 4x4 (because mat3(mat4matrix) does not work on ATI!)
|
||||||
|
mat3 GetLinearPart( mat4 m )
|
||||||
|
{
|
||||||
|
mat3 result;
|
||||||
|
|
||||||
|
result[0][0] = m[0][0];
|
||||||
|
result[0][1] = m[0][1];
|
||||||
|
result[0][2] = m[0][2];
|
||||||
|
|
||||||
|
result[1][0] = m[1][0];
|
||||||
|
result[1][1] = m[1][1];
|
||||||
|
result[1][2] = m[1][2];
|
||||||
|
|
||||||
|
result[2][0] = m[2][0];
|
||||||
|
result[2][1] = m[2][1];
|
||||||
|
result[2][2] = m[2][2];
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
vec3 viewVertex; // vertex coordinates in view space
|
||||||
|
vec3 viewNormal; // vertex normal in view space
|
||||||
|
vec3 sunVector; // sun lighting vector (as reflecting away from vertex)
|
||||||
|
float sunFactor; // sun light projection along vertex normal (0.0 to 1.0)
|
||||||
|
vec3 halfway;
|
||||||
|
float specFactor;
|
||||||
|
|
||||||
|
// Transform vertex
|
||||||
|
gl_Position = projectionMatrix * modelViewMatrix * gl_Vertex;
|
||||||
|
viewVertex = vec3(modelViewMatrix * gl_Vertex);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Modulation
|
||||||
|
*
|
||||||
|
* Polygon color serves as material color (modulating the light intensity)
|
||||||
|
* for textured polygons. The fragment shader will ignore (overwrite) the
|
||||||
|
* the color passed to it if the fragment is textured.
|
||||||
|
*
|
||||||
|
* Untextured fragments must be set to the polygon color and the light
|
||||||
|
* intensity is initialized to 1.0 here. Alpha must be set to 1.0 because
|
||||||
|
* the fragment shader multiplies it by the polygon translucency setting.
|
||||||
|
*
|
||||||
|
* TO-DO: Does OpenGL set alpha to 1.0 by default if no alpha is specified
|
||||||
|
* for the vertex? If so, we can remove that line from here.
|
||||||
|
*/
|
||||||
|
|
||||||
|
gl_FrontColor = gl_Color; // untextured polygons will use this
|
||||||
|
gl_FrontColor.a = 1.0;
|
||||||
|
fsLightIntensity = vec3(1.0,1.0,1.0);
|
||||||
|
if (texParams.x > 0.0) // textured
|
||||||
|
fsLightIntensity *= gl_Color.rgb;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sun Light
|
||||||
|
*
|
||||||
|
* Parallel light source and ambient lighting are only applied for non-
|
||||||
|
* luminous polygons.
|
||||||
|
*/
|
||||||
|
if (lightEnable > 0.5) // not luminous
|
||||||
|
{
|
||||||
|
// Normal -> view space
|
||||||
|
viewNormal = normalize(GetLinearPart(modelViewMatrix)*gl_Normal);
|
||||||
|
|
||||||
|
// Real3D -> OpenGL view space convention (TO-DO: do this outside of shader)
|
||||||
|
sunVector = lighting[0]*vec3(1.0,-1.0,-1.0);
|
||||||
|
|
||||||
|
// Compute diffuse factor for sunlight
|
||||||
|
sunFactor = max(dot(sunVector,viewNormal),0.0);
|
||||||
|
|
||||||
|
// Total light intensity: sum of all components
|
||||||
|
fsLightIntensity *= (sunFactor*lighting[1].x+lighting[1].y);
|
||||||
|
fsLightIntensity = clamp(fsLightIntensity,0.0,1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fog
|
||||||
|
float z = length(viewVertex);
|
||||||
|
fsFogFactor = clamp(1.0-fogIntensity*(gl_Fog.start+z*gl_Fog.density), 0.0, 1.0);
|
||||||
|
|
||||||
|
// Pass viewspace Z coordinate (for spotlight)
|
||||||
|
fsViewZ = -viewVertex.z; // convert Z from GL->Real3D convention (want +Z to be further into screen)
|
||||||
|
|
||||||
|
// Pass remaining parameters to fragment shader
|
||||||
|
gl_TexCoord[0] = gl_MultiTexCoord0;
|
||||||
|
fsSubTexture = subTexture;
|
||||||
|
fsTexParams = texParams;
|
||||||
|
fsTransLevel = transLevel;
|
||||||
|
fsTexFormat = texFormat;
|
||||||
|
}
|
34
Graphics/Shaders/Vertex2D.glsl
Normal file
34
Graphics/Shaders/Vertex2D.glsl
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Vertex2D.glsl
|
||||||
|
*
|
||||||
|
* Vertex shader for 2D tilemap rendering.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#version 120
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
gl_TexCoord[0] = gl_MultiTexCoord0;
|
||||||
|
gl_Position = gl_ModelViewProjectionMatrix*gl_Vertex;
|
||||||
|
}
|
119
Graphics/Shaders2D.h
Normal file
119
Graphics/Shaders2D.h
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Shaders2D.h
|
||||||
|
*
|
||||||
|
* Header file containing the 2D vertex and fragment shaders.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDED_SHADERS2D_H
|
||||||
|
#define INCLUDED_SHADERS2D_H
|
||||||
|
|
||||||
|
// Vertex shader
|
||||||
|
static const char vertexShaderSource[] =
|
||||||
|
{
|
||||||
|
"/** \n"
|
||||||
|
" ** Supermodel \n"
|
||||||
|
" ** A Sega Model 3 Arcade Emulator. \n"
|
||||||
|
" ** Copyright 2011 Bart Trzynadlowski \n"
|
||||||
|
" ** \n"
|
||||||
|
" ** This file is part of Supermodel. \n"
|
||||||
|
" ** \n"
|
||||||
|
" ** Supermodel is free software: you can redistribute it and/or modify it under \n"
|
||||||
|
" ** the terms of the GNU General Public License as published by the Free \n"
|
||||||
|
" ** Software Foundation, either version 3 of the License, or (at your option) \n"
|
||||||
|
" ** any later version. \n"
|
||||||
|
" ** \n"
|
||||||
|
" ** Supermodel is distributed in the hope that it will be useful, but WITHOUT \n"
|
||||||
|
" ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or \n"
|
||||||
|
" ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for \n"
|
||||||
|
" ** more details. \n"
|
||||||
|
" ** \n"
|
||||||
|
" ** You should have received a copy of the GNU General Public License along \n"
|
||||||
|
" ** with Supermodel. If not, see <http://www.gnu.org/licenses/>. \n"
|
||||||
|
" **/ \n"
|
||||||
|
"\n"
|
||||||
|
"/* \n"
|
||||||
|
" * Vertex2D.glsl \n"
|
||||||
|
" * \n"
|
||||||
|
" * Vertex shader for 2D tilemap rendering. \n"
|
||||||
|
" */ \n"
|
||||||
|
" \n"
|
||||||
|
"#version 120 \n"
|
||||||
|
"\n"
|
||||||
|
"void main(void) \n"
|
||||||
|
"{ \n"
|
||||||
|
" gl_TexCoord[0] = gl_MultiTexCoord0; \n"
|
||||||
|
" gl_Position = gl_ModelViewProjectionMatrix*gl_Vertex; \n"
|
||||||
|
"}\n"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fragment shader
|
||||||
|
static const char fragmentShaderSource[] =
|
||||||
|
{
|
||||||
|
"/** \n"
|
||||||
|
" ** Supermodel \n"
|
||||||
|
" ** A Sega Model 3 Arcade Emulator. \n"
|
||||||
|
" ** Copyright 2011 Bart Trzynadlowski \n"
|
||||||
|
" ** \n"
|
||||||
|
" ** This file is part of Supermodel. \n"
|
||||||
|
" ** \n"
|
||||||
|
" ** Supermodel is free software: you can redistribute it and/or modify it under \n"
|
||||||
|
" ** the terms of the GNU General Public License as published by the Free \n"
|
||||||
|
" ** Software Foundation, either version 3 of the License, or (at your option) \n"
|
||||||
|
" ** any later version. \n"
|
||||||
|
" ** \n"
|
||||||
|
" ** Supermodel is distributed in the hope that it will be useful, but WITHOUT \n"
|
||||||
|
" ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or \n"
|
||||||
|
" ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for \n"
|
||||||
|
" ** more details. \n"
|
||||||
|
" ** \n"
|
||||||
|
" ** You should have received a copy of the GNU General Public License along \n"
|
||||||
|
" ** with Supermodel. If not, see <http://www.gnu.org/licenses/>. \n"
|
||||||
|
" **/ \n"
|
||||||
|
"\n"
|
||||||
|
"/* \n"
|
||||||
|
" * Fragment2D.glsl \n"
|
||||||
|
" * \n"
|
||||||
|
" * Fragment shader for 2D tilemap rendering. \n"
|
||||||
|
" */ \n"
|
||||||
|
"\n"
|
||||||
|
"#version 120 \n"
|
||||||
|
"\n"
|
||||||
|
"// Global uniforms \n"
|
||||||
|
"uniform sampler2D textureMap; // 512x512 layer surface \n"
|
||||||
|
"uniform vec3 colorOffset; // color offset for this layer \n"
|
||||||
|
"\n"
|
||||||
|
"/* \n"
|
||||||
|
" * main(): \n"
|
||||||
|
" * \n"
|
||||||
|
" * Fragment shader entry point. \n"
|
||||||
|
" */ \n"
|
||||||
|
"\n"
|
||||||
|
"void main(void) \n"
|
||||||
|
"{ \n"
|
||||||
|
" gl_FragColor = texture2D(textureMap, gl_TexCoord[0].st); \n"
|
||||||
|
" gl_FragColor.rgb = clamp(gl_FragColor.rgb+colorOffset,0.0,1.0); \n"
|
||||||
|
"}\n"
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // INCLUDED_SHADERS2D_H
|
374
Graphics/Shaders3D.h
Normal file
374
Graphics/Shaders3D.h
Normal file
|
@ -0,0 +1,374 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Shaders3D.h
|
||||||
|
*
|
||||||
|
* Header file containing the 3D vertex and fragment shaders.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDED_SHADERS3D_H
|
||||||
|
#define INCLUDED_SHADERS3D_H
|
||||||
|
|
||||||
|
// Vertex shader
|
||||||
|
static const char vertexShaderSource[] =
|
||||||
|
{
|
||||||
|
"/** \n"
|
||||||
|
" ** Supermodel \n"
|
||||||
|
" ** A Sega Model 3 Arcade Emulator. \n"
|
||||||
|
" ** Copyright 2011 Bart Trzynadlowski \n"
|
||||||
|
" ** \n"
|
||||||
|
" ** This file is part of Supermodel. \n"
|
||||||
|
" ** \n"
|
||||||
|
" ** Supermodel is free software: you can redistribute it and/or modify it under \n"
|
||||||
|
" ** the terms of the GNU General Public License as published by the Free \n"
|
||||||
|
" ** Software Foundation, either version 3 of the License, or (at your option) \n"
|
||||||
|
" ** any later version. \n"
|
||||||
|
" ** \n"
|
||||||
|
" ** Supermodel is distributed in the hope that it will be useful, but WITHOUT \n"
|
||||||
|
" ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or \n"
|
||||||
|
" ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for \n"
|
||||||
|
" ** more details. \n"
|
||||||
|
" ** \n"
|
||||||
|
" ** You should have received a copy of the GNU General Public License along \n"
|
||||||
|
" ** with Supermodel. If not, see <http://www.gnu.org/licenses/>. \n"
|
||||||
|
" **/ \n"
|
||||||
|
"\n"
|
||||||
|
"/* \n"
|
||||||
|
" * Vertex.glsl \n"
|
||||||
|
" * \n"
|
||||||
|
" * Vertex shader for 3D rendering. \n"
|
||||||
|
" */ \n"
|
||||||
|
"\n"
|
||||||
|
"#version 120 \n"
|
||||||
|
"\n"
|
||||||
|
"// Global uniforms \n"
|
||||||
|
"uniform mat4 modelViewMatrix; // model -> view space matrix \n"
|
||||||
|
"uniform mat4 projectionMatrix; // view space -> screen space matrix \n"
|
||||||
|
"uniform vec3 lighting[2]; // lighting state (lighting[0] = sun direction, lighting[1].x,y = diffuse, ambient intensities from 0-1.0) \n"
|
||||||
|
"uniform vec4 spotEllipse; // spotlight ellipse position: .x=X position (normalized device coordinates), .y=Y position, .z=half-width, .w=half-height) \n"
|
||||||
|
"uniform vec2 spotRange; // spotlight Z range: .x=start (viewspace coordinates), .y=limit \n"
|
||||||
|
"uniform vec3 spotColor; // spotlight RGB color \n"
|
||||||
|
"\n"
|
||||||
|
"// Custom vertex attributes \n"
|
||||||
|
"attribute vec4 subTexture; // .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels) \n"
|
||||||
|
"attribute vec4 texParams; // .x=texture enable (if 1, else 0), .y=use transparency (if >=0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode \n"
|
||||||
|
"attribute float texFormat; // .x=T1RGB5 contour texture (if > 0) \n"
|
||||||
|
"attribute float transLevel; // translucence level, 0.0 (transparent) to 1.0 (opaque). if less than 1.0, replace alpha value \n"
|
||||||
|
"attribute float lightEnable; // lighting enabled (1.0) or luminous (0.0), drawn at full intensity \n"
|
||||||
|
"attribute float fogIntensity; // fog intensity (1.0, full fog effect, 0.0, no fog) \n"
|
||||||
|
"\n"
|
||||||
|
"// Custom outputs to fragment shader \n"
|
||||||
|
"varying vec4 fsSubTexture; \n"
|
||||||
|
"varying vec4 fsTexParams; \n"
|
||||||
|
"varying float fsTexFormat; \n"
|
||||||
|
"varying float fsTransLevel; \n"
|
||||||
|
"varying vec3 fsLightIntensity; // total light intensity for this vertex \n"
|
||||||
|
"varying float fsFogFactor; // fog factor \n"
|
||||||
|
"varying float fsViewZ; \n"
|
||||||
|
"\n"
|
||||||
|
"// Gets the 3x3 matrix out of a 4x4 (because mat3(mat4matrix) does not work on ATI!) \n"
|
||||||
|
"mat3 GetLinearPart( mat4 m ) \n"
|
||||||
|
"{ \n"
|
||||||
|
" mat3 result; \n"
|
||||||
|
"\n"
|
||||||
|
" result[0][0] = m[0][0]; \n"
|
||||||
|
" result[0][1] = m[0][1]; \n"
|
||||||
|
" result[0][2] = m[0][2]; \n"
|
||||||
|
"\n"
|
||||||
|
" result[1][0] = m[1][0]; \n"
|
||||||
|
" result[1][1] = m[1][1]; \n"
|
||||||
|
" result[1][2] = m[1][2]; \n"
|
||||||
|
"\n"
|
||||||
|
" result[2][0] = m[2][0]; \n"
|
||||||
|
" result[2][1] = m[2][1]; \n"
|
||||||
|
" result[2][2] = m[2][2]; \n"
|
||||||
|
"\n"
|
||||||
|
" return result; \n"
|
||||||
|
"} \n"
|
||||||
|
"\n"
|
||||||
|
"void main(void) \n"
|
||||||
|
"{ \n"
|
||||||
|
" vec3 viewVertex; // vertex coordinates in view space \n"
|
||||||
|
" vec3 viewNormal; // vertex normal in view space \n"
|
||||||
|
" vec3 sunVector; // sun lighting vector (as reflecting away from vertex) \n"
|
||||||
|
" float sunFactor; // sun light projection along vertex normal (0.0 to 1.0) \n"
|
||||||
|
" vec3 halfway; \n"
|
||||||
|
" float specFactor; \n"
|
||||||
|
" \n"
|
||||||
|
" // Transform vertex \n"
|
||||||
|
" gl_Position = projectionMatrix * modelViewMatrix * gl_Vertex; \n"
|
||||||
|
" viewVertex = vec3(modelViewMatrix * gl_Vertex); \n"
|
||||||
|
" \n"
|
||||||
|
" /* \n"
|
||||||
|
" * Modulation \n"
|
||||||
|
" * \n"
|
||||||
|
" * Polygon color serves as material color (modulating the light intensity) \n"
|
||||||
|
" * for textured polygons. The fragment shader will ignore (overwrite) the \n"
|
||||||
|
" * the color passed to it if the fragment is textured. \n"
|
||||||
|
" * \n"
|
||||||
|
" * Untextured fragments must be set to the polygon color and the light \n"
|
||||||
|
" * intensity is initialized to 1.0 here. Alpha must be set to 1.0 because \n"
|
||||||
|
" * the fragment shader multiplies it by the polygon translucency setting. \n"
|
||||||
|
" * \n"
|
||||||
|
" * To-do: Does OpenGL set alpha to 1.0 by default if no alpha is specified \n"
|
||||||
|
" * for the vertex? If so, we can remove that line from here. \n"
|
||||||
|
" */ \n"
|
||||||
|
" \n"
|
||||||
|
" gl_FrontColor = gl_Color; // untextured polygons will use this \n"
|
||||||
|
" gl_FrontColor.a = 1.0; \n"
|
||||||
|
" fsLightIntensity = vec3(1.0,1.0,1.0); \n"
|
||||||
|
" if (texParams.x > 0.0) // textured \n"
|
||||||
|
" fsLightIntensity *= gl_Color.rgb; \n"
|
||||||
|
" \n"
|
||||||
|
" /* \n"
|
||||||
|
" * Sun Light \n"
|
||||||
|
" * \n"
|
||||||
|
" * Parallel light source and ambient lighting are only applied for non- \n"
|
||||||
|
" * luminous polygons. \n"
|
||||||
|
" */ \n"
|
||||||
|
" if (lightEnable > 0.5) // not luminous \n"
|
||||||
|
" { \n"
|
||||||
|
" // Normal -> view space \n"
|
||||||
|
" viewNormal = normalize(GetLinearPart(modelViewMatrix)*gl_Normal); \n"
|
||||||
|
" \n"
|
||||||
|
" // Real3D -> OpenGL view space convention (TO-DO: do this outside of shader) \n"
|
||||||
|
" sunVector = lighting[0]*vec3(1.0,-1.0,-1.0); \n"
|
||||||
|
" \n"
|
||||||
|
" // Compute diffuse factor for sunlight \n"
|
||||||
|
" sunFactor = max(dot(sunVector,viewNormal),0.0); \n"
|
||||||
|
" \n"
|
||||||
|
" // Total light intensity: sum of all components \n"
|
||||||
|
" fsLightIntensity *= (sunFactor*lighting[1].x+lighting[1].y); \n"
|
||||||
|
" fsLightIntensity = clamp(fsLightIntensity,0.0,1.0); \n"
|
||||||
|
" } \n"
|
||||||
|
" \n"
|
||||||
|
" // Fog \n"
|
||||||
|
" float z = length(viewVertex); \n"
|
||||||
|
" fsFogFactor = clamp(1.0-fogIntensity*(gl_Fog.start+z*gl_Fog.density), 0.0, 1.0); \n"
|
||||||
|
" \n"
|
||||||
|
" // Pass viewspace Z coordinate (for spotlight) \n"
|
||||||
|
" fsViewZ = -viewVertex.z; // convert Z from GL->Real3D convention (want +Z to be further into screen) \n"
|
||||||
|
" \n"
|
||||||
|
" // Pass remaining parameters to fragment shader \n"
|
||||||
|
" gl_TexCoord[0] = gl_MultiTexCoord0; \n"
|
||||||
|
" fsSubTexture = subTexture; \n"
|
||||||
|
" fsTexParams = texParams; \n"
|
||||||
|
" fsTransLevel = transLevel; \n"
|
||||||
|
" fsTexFormat = texFormat; \n"
|
||||||
|
"}\n"
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Fragment shader
|
||||||
|
static const char fragmentShaderSource[] =
|
||||||
|
{
|
||||||
|
"/** \n"
|
||||||
|
" ** Supermodel \n"
|
||||||
|
" ** A Sega Model 3 Arcade Emulator. \n"
|
||||||
|
" ** Copyright 2011 Bart Trzynadlowski \n"
|
||||||
|
" ** \n"
|
||||||
|
" ** This file is part of Supermodel. \n"
|
||||||
|
" ** \n"
|
||||||
|
" ** Supermodel is free software: you can redistribute it and/or modify it under \n"
|
||||||
|
" ** the terms of the GNU General Public License as published by the Free \n"
|
||||||
|
" ** Software Foundation, either version 3 of the License, or (at your option) \n"
|
||||||
|
" ** any later version. \n"
|
||||||
|
" ** \n"
|
||||||
|
" ** Supermodel is distributed in the hope that it will be useful, but WITHOUT \n"
|
||||||
|
" ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or \n"
|
||||||
|
" ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for \n"
|
||||||
|
" ** more details. \n"
|
||||||
|
" ** \n"
|
||||||
|
" ** You should have received a copy of the GNU General Public License along \n"
|
||||||
|
" ** with Supermodel. If not, see <http://www.gnu.org/licenses/>. \n"
|
||||||
|
" **/ \n"
|
||||||
|
" \n"
|
||||||
|
"/* \n"
|
||||||
|
" * Fragment.glsl \n"
|
||||||
|
" * \n"
|
||||||
|
" * Fragment shader for 3D rendering. \n"
|
||||||
|
" */ \n"
|
||||||
|
" \n"
|
||||||
|
"#version 120 \n"
|
||||||
|
" \n"
|
||||||
|
"// Global uniforms \n"
|
||||||
|
"uniform sampler2D textureMap; // complete texture map, 2048x2048 texels \n"
|
||||||
|
"uniform vec4 spotEllipse; // spotlight ellipse position: .x=X position (normalized device coordinates), .y=Y position, .z=half-width, .w=half-height) \n"
|
||||||
|
"uniform vec2 spotRange; // spotlight Z range: .x=start (viewspace coordinates), .y=limit \n"
|
||||||
|
"uniform vec3 spotColor; // spotlight RGB color \n"
|
||||||
|
"\n"
|
||||||
|
"// Inputs from vertex shader \n"
|
||||||
|
"varying vec4 fsSubTexture; // .x=texture X, .y=texture Y, .z=texture width, .w=texture height (all in texels) \n"
|
||||||
|
"varying vec4 fsTexParams; // .x=texture enable (if 1, else 0), .y=use transparency (if > 0), .z=U wrap mode (1=mirror, 0=repeat), .w=V wrap mode \n"
|
||||||
|
"varying float fsTexFormat; // .x=T1RGB5 contour texture (if > 0) \n"
|
||||||
|
"varying float fsTransLevel; // translucence level, 0.0 (transparent) to 1.0 (opaque) \n"
|
||||||
|
"varying vec3 fsLightIntensity; // lighting intensity \n"
|
||||||
|
"varying float fsFogFactor; // fog factor \n"
|
||||||
|
"varying float fsViewZ; // Z distance to fragment from viewpoint at origin \n"
|
||||||
|
"\n"
|
||||||
|
"/* \n"
|
||||||
|
" * WrapTexelCoords(): \n"
|
||||||
|
" * \n"
|
||||||
|
" * Computes the normalized OpenGL S,T coordinates within the 2048x2048 texture \n"
|
||||||
|
" * sheet, taking into account wrapping behavior. \n"
|
||||||
|
" * \n"
|
||||||
|
" * Computing normalized OpenGL texture coordinates (0 to 1) within the \n"
|
||||||
|
" * Real3D texture sheet: \n"
|
||||||
|
" * \n"
|
||||||
|
" * If the texture is not mirrored, we simply have to clamp the \n"
|
||||||
|
" * coordinates to fit within the texture dimensions, add the texture \n"
|
||||||
|
" * X, Y position to select the appropriate one, and normalize by 2048 \n"
|
||||||
|
" * (the dimensions of the Real3D texture sheet). \n"
|
||||||
|
" * \n"
|
||||||
|
" * = [(u,v)%(w,h)+(x,y)]/(2048,2048) \n"
|
||||||
|
" * \n"
|
||||||
|
" * If mirroring is enabled, textures are mirrored every odd multiple of \n"
|
||||||
|
" * the original texture. To detect whether we are in an odd multiple, \n"
|
||||||
|
" * simply divide the coordinate by the texture dimension and check \n"
|
||||||
|
" * whether the result is odd. Then, clamp the coordinates as before but \n"
|
||||||
|
" * subtract from the last texel to mirror them: \n"
|
||||||
|
" * \n"
|
||||||
|
" * = [M*((w-1,h-1)-(u,v)%(w,h)) + (1-M)*(u,v)%(w,h) + (x,y)]/(2048,2048) \n"
|
||||||
|
" * where M is 1.0 if the texture must be mirrored. \n"
|
||||||
|
" * \n"
|
||||||
|
" * As an optimization, this function computes TWO texture coordinates \n"
|
||||||
|
" * simultaneously. The first is texCoord.xy, the second is in .zw. The other \n"
|
||||||
|
" * parameters must have .xy = .zw. \n"
|
||||||
|
" */ \n"
|
||||||
|
"vec4 WrapTexelCoords(vec4 texCoord, vec4 texOffset, vec4 texSize, vec4 mirrorEnable) \n"
|
||||||
|
"{ \n"
|
||||||
|
" vec4 clampedCoord, mirror, glTexCoord; \n"
|
||||||
|
" \n"
|
||||||
|
" clampedCoord = mod(texCoord,texSize); // clamp coordinates to within texture size \n"
|
||||||
|
" mirror = mirrorEnable * mod(floor(texCoord/texSize),2.0); // whether this texel needs to be mirrored \n"
|
||||||
|
" \n"
|
||||||
|
" glTexCoord = ( mirror*(texSize-clampedCoord) + \n"
|
||||||
|
" (vec4(1.0,1.0,1.0,1.0)-mirror)*clampedCoord + \n"
|
||||||
|
" texOffset \n"
|
||||||
|
" ) / 2048.0; \n"
|
||||||
|
" return glTexCoord; \n"
|
||||||
|
"} \n"
|
||||||
|
"\n"
|
||||||
|
"/* \n"
|
||||||
|
" * main(): \n"
|
||||||
|
" * \n"
|
||||||
|
" * Fragment shader entry point. \n"
|
||||||
|
" */ \n"
|
||||||
|
"\n"
|
||||||
|
"void main(void) \n"
|
||||||
|
"{ \n"
|
||||||
|
" vec4 uv_top, uv_bot, c[4]; \n"
|
||||||
|
" vec2 r; \n"
|
||||||
|
" vec4 fragColor; \n"
|
||||||
|
" vec2 ellipse; \n"
|
||||||
|
" vec3 lightIntensity; \n"
|
||||||
|
" float insideSpot; \n"
|
||||||
|
" \n"
|
||||||
|
" // Get polygon color for untextured polygons (textured polygons will overwrite) \n"
|
||||||
|
" if (fsTexParams.x==0.0) \n"
|
||||||
|
" fragColor = gl_Color; \n"
|
||||||
|
" else \n"
|
||||||
|
" // Textured polygons: set fragment color to texel value \n"
|
||||||
|
" { \n"
|
||||||
|
" /* \n"
|
||||||
|
" * Bilinear Filtering \n"
|
||||||
|
" * \n"
|
||||||
|
" * In order to get this working on ATI, the number of operations is \n"
|
||||||
|
" * reduced by putting everything into vec4s. uv_top holds the UV \n"
|
||||||
|
" * coordinates for the top two texels (.xy=left, .zw=right) and uv_bot \n"
|
||||||
|
" * is for the lower two. \n"
|
||||||
|
" */ \n"
|
||||||
|
" \n"
|
||||||
|
" // Compute fractional blending factor, r, and lower left corner of texel 0 \n"
|
||||||
|
" uv_bot.xy = gl_TexCoord[0].st-vec2(0.5,0.5); // move into the lower left blending texel \n"
|
||||||
|
" r = uv_bot.xy-floor(uv_bot.xy); // fractional part \n"
|
||||||
|
" uv_bot.xy = floor(uv_bot.xy); // integral part \n"
|
||||||
|
" \n"
|
||||||
|
" // Compute texel coordinates \n"
|
||||||
|
" uv_bot.xy += vec2(0.5,0.5); // offset to center of pixel (should not be needed but it fixes a lot of glitches, esp. on Nvidia) \n"
|
||||||
|
" uv_bot.zw = uv_bot.xy + vec2(1.0,0.0); // compute coordinates of the other three neighbors \n"
|
||||||
|
" uv_top = uv_bot + vec4(0.0,1.0,0.0,1.0); \n"
|
||||||
|
" \n"
|
||||||
|
" // Compute the properly wrapped texel coordinates \n"
|
||||||
|
" uv_top = WrapTexelCoords(uv_top,vec4(fsSubTexture.xy,fsSubTexture.xy),vec4(fsSubTexture.zw,fsSubTexture.zw), vec4(fsTexParams.zw,fsTexParams.zw)); \n"
|
||||||
|
" uv_bot = WrapTexelCoords(uv_bot,vec4(fsSubTexture.xy,fsSubTexture.xy),vec4(fsSubTexture.zw,fsSubTexture.zw), vec4(fsTexParams.zw,fsTexParams.zw)); \n"
|
||||||
|
" \n"
|
||||||
|
" // Fetch the texels \n"
|
||||||
|
" c[0]=texture2D(textureMap,uv_bot.xy); // bottom-left (base texel) \n"
|
||||||
|
" c[1]=texture2D(textureMap,uv_bot.zw); // bottom-right \n"
|
||||||
|
" c[2]=texture2D(textureMap,uv_top.xy); // top-left \n"
|
||||||
|
" c[3]=texture2D(textureMap,uv_top.zw); // top-right \n"
|
||||||
|
" \n"
|
||||||
|
" // Interpolate texels and blend result with material color to determine final (unlit) fragment color \n"
|
||||||
|
" // fragColor = (c[0]*(1.0-r.s)*(1.0-r.t) + c[1]*r.s*(1.0-r.t) + c[2]*(1.0-r.s)*r.t + c[3]*r.s*r.t); \n"
|
||||||
|
" // Faster method: \n"
|
||||||
|
" c[0] += (c[1]-c[0])*r.s; // 2 alu \n"
|
||||||
|
" c[2] += (c[3]-c[2])*r.s; // 2 alu \n"
|
||||||
|
" fragColor = c[0]+(c[2]-c[0])*r.t; // 2 alu \n"
|
||||||
|
" \n"
|
||||||
|
" /* \n"
|
||||||
|
" * T1RGB5: \n"
|
||||||
|
" * \n"
|
||||||
|
" * The transparency bit determines whether to discard pixels (if set). \n"
|
||||||
|
" * What is unknown is how this bit behaves when interpolated. OpenGL \n"
|
||||||
|
" * processes it as an alpha value, so it might concievably be blended \n"
|
||||||
|
" * with neighbors. Here, an arbitrary threshold is chosen. \n"
|
||||||
|
" * \n"
|
||||||
|
" * To-do: blending could probably enabled and this would work even \n"
|
||||||
|
" * better with a hard threshold. \n"
|
||||||
|
" * \n"
|
||||||
|
" * Countour processing also seems to be enabled for RGBA4 textures. \n"
|
||||||
|
" * When the alpha value is 0.0 (or close), pixels are discarded \n"
|
||||||
|
" * entirely. \n"
|
||||||
|
" */ \n"
|
||||||
|
" if (fsTexParams.y > 0.0) // contour processing enabled \n"
|
||||||
|
" { \n"
|
||||||
|
" if (fragColor.a < 0.01) // discard anything with alpha == 0 \n"
|
||||||
|
" discard; \n"
|
||||||
|
" } \n"
|
||||||
|
" \n"
|
||||||
|
" // If contour texture and not discarded, force alpha to 1.0 because will later be modified by polygon translucency \n"
|
||||||
|
" if (fsTexFormat > 0.0) // contour (T1RGB5) texture map \n"
|
||||||
|
" fragColor.a = 1.0; \n"
|
||||||
|
" } \n"
|
||||||
|
" \n"
|
||||||
|
" // Compute spotlight and apply lighting \n"
|
||||||
|
" ellipse = (gl_FragCoord.xy-spotEllipse.xy)/spotEllipse.zw; \n"
|
||||||
|
" insideSpot = dot(ellipse,ellipse); \n"
|
||||||
|
" if ((insideSpot <= 1.0) && (fsViewZ>=spotRange.x) && (fsViewZ<spotRange.y)) \n"
|
||||||
|
" lightIntensity = min(fsLightIntensity+(1.0-insideSpot)*spotColor,1.0); \n"
|
||||||
|
" else \n"
|
||||||
|
" lightIntensity = fsLightIntensity; \n"
|
||||||
|
" fragColor.rgb *= lightIntensity; \n"
|
||||||
|
" \n"
|
||||||
|
" // Translucency (modulates existing alpha channel for RGBA4 texels) \n"
|
||||||
|
" fragColor.a *= fsTransLevel; \n"
|
||||||
|
" \n"
|
||||||
|
" // Apply fog under the control of fog factor setting from polygon header \n"
|
||||||
|
" fragColor.rgb = mix(gl_Fog.color.rgb, fragColor.rgb, fsFogFactor ); \n"
|
||||||
|
" \n"
|
||||||
|
" // Store final color \n"
|
||||||
|
" gl_FragColor = fragColor; \n"
|
||||||
|
"}\n"
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // INCLUDED_SHADERS3D_H
|
580
INIFile.cpp
Normal file
580
INIFile.cpp
Normal file
|
@ -0,0 +1,580 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* INIFile.cpp
|
||||||
|
*
|
||||||
|
* INI file management. Implementation of the CINIFile class.
|
||||||
|
*
|
||||||
|
* To-Do List
|
||||||
|
* ----------
|
||||||
|
* - Add boolean on/off, true/false keywords.
|
||||||
|
* - Allow a "default" section to be specified (other than "").
|
||||||
|
* - Note that linePtr does not necessarily correspond to actual lines in the
|
||||||
|
* file (newlines should be counted by the tokenizer for that).
|
||||||
|
*
|
||||||
|
* Grammar
|
||||||
|
* -------
|
||||||
|
*
|
||||||
|
* Section: '[' Identifier ']'
|
||||||
|
* Line: Identifier '=' Argument
|
||||||
|
* Argument: Number
|
||||||
|
* String
|
||||||
|
*
|
||||||
|
* Overview
|
||||||
|
* --------
|
||||||
|
* INI files are linear in nature, divided into sections. Each section has
|
||||||
|
* settings associated with it which may be either numerical values or strings.
|
||||||
|
* Presently, only unsigned 32-bit integers are supported.
|
||||||
|
*
|
||||||
|
* INI files are first opened, then parsed, and finally closed. The parse tree
|
||||||
|
* is cleared only during object construction and when the INI file is closed.
|
||||||
|
* Default settings may be inserted before a file is opened or parsed. When an
|
||||||
|
* INI file is written out, the current INI file on the disk is cleared. If an
|
||||||
|
* error occurs during this process, the data will be lost.
|
||||||
|
*
|
||||||
|
* Blank sections (sections for which no settings are defined) are not stored
|
||||||
|
* in the parse tree. Sections are added only when a setting is found in that
|
||||||
|
* section. A default section name can be specified (for settings that are not
|
||||||
|
* associated with any section), otherwise an empty string ("") is used.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Supermodel.h"
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Basic Functions
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
BOOL CINIFile::Write(const char *comment)
|
||||||
|
{
|
||||||
|
BOOL writeSuccess;
|
||||||
|
|
||||||
|
// In order to truncate, we must close and reopen as truncated
|
||||||
|
File.close();
|
||||||
|
File.clear(); // required to clear EOF flag (open() does not clear)
|
||||||
|
File.open(FileName.c_str(),fstream::out|fstream::trunc);
|
||||||
|
if (File.fail())
|
||||||
|
{
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output comment
|
||||||
|
if (comment != NULL)
|
||||||
|
File << comment << endl;
|
||||||
|
|
||||||
|
// Iterate through all sections sequentially
|
||||||
|
for (unsigned i = 0; i < Sections.size(); i++)
|
||||||
|
{
|
||||||
|
// Output section name
|
||||||
|
if (Sections[i].Name != "") // if null name, don't output section at all
|
||||||
|
File << "[ " << Sections[i].Name << " ]" << endl << endl;
|
||||||
|
|
||||||
|
// Iterate through all settings within this section
|
||||||
|
for (unsigned j = 0; j < Sections[i].Settings.size(); j++)
|
||||||
|
{
|
||||||
|
// Output setting
|
||||||
|
File << Sections[i].Settings[j].Name << " = ";
|
||||||
|
if (Sections[i].Settings[j].isNumber)
|
||||||
|
File << Sections[i].Settings[j].value << endl;
|
||||||
|
else
|
||||||
|
File << '\"' << Sections[i].Settings[j].String << '\"' << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// New line
|
||||||
|
File << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeSuccess = File.good()?OKAY:FAIL;
|
||||||
|
|
||||||
|
// Close and reopen as read/write
|
||||||
|
File.close();
|
||||||
|
File.open(FileName.c_str(),fstream::in|fstream::out);
|
||||||
|
if (File.fail())
|
||||||
|
{
|
||||||
|
//printf("unable to re-open %s for reading/writing\n", FileName.c_str());
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Report any errors that occurred during writing
|
||||||
|
return writeSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL CINIFile::Open(const char *fileNameStr)
|
||||||
|
{
|
||||||
|
FileName = fileNameStr;
|
||||||
|
File.open(fileNameStr, fstream::in|fstream::out);
|
||||||
|
if (File.fail())
|
||||||
|
{
|
||||||
|
//printf("unable to open %s\n", fileNameStr);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
return OKAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CINIFile::Close(void)
|
||||||
|
{
|
||||||
|
File.close();
|
||||||
|
Sections.clear(); // clear the parse tree!
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Management of Settings
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
// Finds index of section in the section list. Returns FAIL if not found.
|
||||||
|
BOOL CINIFile::LookUpSection(unsigned *idx, string SectionName)
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < Sections.size(); i++)
|
||||||
|
{
|
||||||
|
if (Sections[i].Name == SectionName)
|
||||||
|
{
|
||||||
|
*idx = i;
|
||||||
|
return OKAY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finds index of the setting in the given section. Returns FAIL if not found.
|
||||||
|
BOOL CINIFile::LookUpSetting(unsigned *idx, unsigned sectionIdx, string SettingName)
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < Sections[sectionIdx].Settings.size(); i++)
|
||||||
|
{
|
||||||
|
if (Sections[sectionIdx].Settings[i].Name == SettingName)
|
||||||
|
{
|
||||||
|
*idx = i;
|
||||||
|
return OKAY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Assigns a value to the given setting, creating the setting if it does not exist. Nulls out the string (sets it to "").
|
||||||
|
void CINIFile::Set(string SectionName, string SettingName, unsigned value)
|
||||||
|
{
|
||||||
|
unsigned sectionIdx, settingIdx;
|
||||||
|
|
||||||
|
if (OKAY != LookUpSection(§ionIdx, SectionName))
|
||||||
|
{
|
||||||
|
//printf("unable to find %s:%s, creating section\n", SectionName.c_str(), SettingName.c_str());
|
||||||
|
struct Section NewSection;
|
||||||
|
|
||||||
|
NewSection.Name = SectionName;
|
||||||
|
Sections.push_back(NewSection);
|
||||||
|
sectionIdx = Sections.size()-1; // the new section will be at the last index
|
||||||
|
}
|
||||||
|
|
||||||
|
if (OKAY != LookUpSetting(&settingIdx, sectionIdx, SettingName))
|
||||||
|
{
|
||||||
|
//printf("unable to find %s:%s, creating setting\n", SectionName.c_str(), SettingName.c_str());
|
||||||
|
struct Setting NewSetting;
|
||||||
|
|
||||||
|
NewSetting.Name = SettingName;
|
||||||
|
Sections[sectionIdx].Settings.push_back(NewSetting);
|
||||||
|
settingIdx = Sections[sectionIdx].Settings.size()-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update value of this setting
|
||||||
|
Sections[sectionIdx].Settings[settingIdx].isNumber = TRUE;
|
||||||
|
Sections[sectionIdx].Settings[settingIdx].value = value;
|
||||||
|
Sections[sectionIdx].Settings[settingIdx].String = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assigns the string to the given setting, creating the setting if it does not exist. Zeros out the value.
|
||||||
|
void CINIFile::Set(string SectionName, string SettingName, string String)
|
||||||
|
{
|
||||||
|
unsigned sectionIdx, settingIdx;
|
||||||
|
|
||||||
|
if (OKAY != LookUpSection(§ionIdx, SectionName))
|
||||||
|
{
|
||||||
|
//printf("unable to find %s:%s, creating section\n", SectionName.c_str(), SettingName.c_str());
|
||||||
|
struct Section NewSection;
|
||||||
|
|
||||||
|
NewSection.Name = SectionName;
|
||||||
|
Sections.push_back(NewSection);
|
||||||
|
sectionIdx = Sections.size()-1; // the new section will be at the last index
|
||||||
|
}
|
||||||
|
|
||||||
|
if (OKAY != LookUpSetting(&settingIdx, sectionIdx, SettingName))
|
||||||
|
{
|
||||||
|
//printf("unable to find %s:%s, creating setting\n", SectionName.c_str(), SettingName.c_str());
|
||||||
|
struct Setting NewSetting;
|
||||||
|
|
||||||
|
NewSetting.Name = SettingName;
|
||||||
|
Sections[sectionIdx].Settings.push_back(NewSetting);
|
||||||
|
settingIdx = Sections[sectionIdx].Settings.size()-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update string
|
||||||
|
Sections[sectionIdx].Settings[settingIdx].isNumber = FALSE;
|
||||||
|
Sections[sectionIdx].Settings[settingIdx].String = String;
|
||||||
|
Sections[sectionIdx].Settings[settingIdx].value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtains a numerical setting, if it exists, otherwise does nothing.
|
||||||
|
BOOL CINIFile::Get(string SectionName, string SettingName, unsigned& value)
|
||||||
|
{
|
||||||
|
unsigned sectionIdx, settingIdx;
|
||||||
|
|
||||||
|
if (OKAY != LookUpSection(§ionIdx, SectionName))
|
||||||
|
return FAIL;
|
||||||
|
if (OKAY != LookUpSetting(&settingIdx, sectionIdx, SettingName))
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
value = Sections[sectionIdx].Settings[settingIdx].value;
|
||||||
|
|
||||||
|
return OKAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtains a string setting, if it exists, otherwise does nothing.
|
||||||
|
BOOL CINIFile::Get(string SectionName, string SettingName, string& String)
|
||||||
|
{
|
||||||
|
unsigned sectionIdx, settingIdx;
|
||||||
|
|
||||||
|
if (OKAY != LookUpSection(§ionIdx, SectionName))
|
||||||
|
return FAIL;
|
||||||
|
if (OKAY != LookUpSetting(&settingIdx, sectionIdx, SettingName))
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
String = Sections[sectionIdx].Settings[settingIdx].String;
|
||||||
|
|
||||||
|
return OKAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Tokenizer
|
||||||
|
|
||||||
|
Never need to check for newlines because it is assumed these have been
|
||||||
|
stripped by getline().
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
// Token types
|
||||||
|
#define TOKEN_INVALID -1
|
||||||
|
#define TOKEN_NULL 0
|
||||||
|
#define TOKEN_IDENTIFIER 1
|
||||||
|
#define TOKEN_NUMBER 2
|
||||||
|
#define TOKEN_STRING 3
|
||||||
|
|
||||||
|
|
||||||
|
// Token constructor (initializes token to null)
|
||||||
|
CINIFile::CToken::CToken(void)
|
||||||
|
{
|
||||||
|
type = TOKEN_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true for white space, comment symbol, or null terminator.
|
||||||
|
static BOOL IsBlank(char c)
|
||||||
|
{
|
||||||
|
if (isspace(c) || (c==';') || (c=='\0'))
|
||||||
|
return TRUE;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetches a string. Tolerates all characters between quotes.
|
||||||
|
CINIFile::CToken CINIFile::GetString(void)
|
||||||
|
{
|
||||||
|
CToken T;
|
||||||
|
|
||||||
|
T.type = TOKEN_STRING;
|
||||||
|
|
||||||
|
// Search for next quote
|
||||||
|
++linePtr;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
if (linePtr[0] == '\"')
|
||||||
|
{
|
||||||
|
++linePtr; // so we can find next token
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (linePtr[0] == '\0')
|
||||||
|
{
|
||||||
|
//printf("tokenizer: warning: string is missing end quote\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
T.String += linePtr[0];
|
||||||
|
|
||||||
|
++linePtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch number (decimal or hexadecimal integer). linePtr must point to a character and therefore linePtr[1] is guaranteed to be within bounds.
|
||||||
|
CINIFile::CToken CINIFile::GetNumber(void)
|
||||||
|
{
|
||||||
|
CToken T;
|
||||||
|
unsigned long long number = 0;
|
||||||
|
int overflow = 0;
|
||||||
|
|
||||||
|
T.type = TOKEN_NUMBER;
|
||||||
|
|
||||||
|
// Hexadecimal?
|
||||||
|
if ((linePtr[0]=='0') && ((linePtr[1]=='X') || (linePtr[1]=='x')))
|
||||||
|
{
|
||||||
|
linePtr += 2; // advance to digits
|
||||||
|
|
||||||
|
// Ensure that at we have at least one digit
|
||||||
|
if (!isxdigit(linePtr[0]))
|
||||||
|
{
|
||||||
|
//printf("tokenizer: invalid hexadecimal number\n");
|
||||||
|
T.type = TOKEN_INVALID;
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read number digit by digit
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
if (isxdigit(linePtr[0]))
|
||||||
|
{
|
||||||
|
number <<= 4;
|
||||||
|
if (isdigit(linePtr[0]))
|
||||||
|
number |= (linePtr[0]-'0');
|
||||||
|
else if (isupper(linePtr[0]))
|
||||||
|
number |= (linePtr[0]-'A');
|
||||||
|
else // must be lowercase...
|
||||||
|
number |= (linePtr[0]-'a');
|
||||||
|
++linePtr;
|
||||||
|
|
||||||
|
// Check for overflows
|
||||||
|
if (number > 0x00000000FFFFFFFFULL)
|
||||||
|
overflow = 1;
|
||||||
|
}
|
||||||
|
else if (IsBlank(linePtr[0]))
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//printf("tokenizer: invalid hexadecimal number\n");
|
||||||
|
T.type = TOKEN_INVALID;
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decimal?
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Read number digit by digit
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
if (isdigit(linePtr[0]))
|
||||||
|
{
|
||||||
|
number *= 10;
|
||||||
|
number += (linePtr[0]-'0');
|
||||||
|
++linePtr;
|
||||||
|
|
||||||
|
// Check for overflows
|
||||||
|
if (number > 0x00000000FFFFFFFFULL)
|
||||||
|
overflow = 1;
|
||||||
|
}
|
||||||
|
else if (IsBlank(linePtr[0]))
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//printf("tokenizer: invalid number\n");
|
||||||
|
T.type = TOKEN_INVALID;
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//if (overflow)
|
||||||
|
// printf("tokenizer: number exceeds 32 bits and has been truncated\n");
|
||||||
|
|
||||||
|
T.number = (unsigned) number;
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch identifier
|
||||||
|
CINIFile::CToken CINIFile::GetIdentifier(void)
|
||||||
|
{
|
||||||
|
CToken T;
|
||||||
|
|
||||||
|
T.type = TOKEN_IDENTIFIER;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
if (isalpha(linePtr[0]) || isdigit(linePtr[0]) || (linePtr[0]=='_'))
|
||||||
|
{
|
||||||
|
T.String += linePtr[0];
|
||||||
|
++linePtr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch token
|
||||||
|
CINIFile::CToken CINIFile::GetToken(void)
|
||||||
|
{
|
||||||
|
CToken T;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
// Gobble up whitespace
|
||||||
|
if (isspace(linePtr[0]))
|
||||||
|
++linePtr;
|
||||||
|
|
||||||
|
// Comment or end of line
|
||||||
|
else if ((linePtr[0]==';') || (linePtr[0]=='\0'))
|
||||||
|
{
|
||||||
|
T.type = TOKEN_NULL;
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delimiters
|
||||||
|
else if ((linePtr[0]=='[') || (linePtr[0]==']') || (linePtr[0]=='='))
|
||||||
|
{
|
||||||
|
T.type = *linePtr++;
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Identifier?
|
||||||
|
else if (isalpha(linePtr[0]) || (linePtr[0] == '_'))
|
||||||
|
{
|
||||||
|
T = GetIdentifier();
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Number? (+/-?)
|
||||||
|
else if (isdigit(linePtr[0]))
|
||||||
|
{
|
||||||
|
T = GetNumber();
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
|
// String?
|
||||||
|
else if (linePtr[0]=='\"')
|
||||||
|
{
|
||||||
|
T = GetString();
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Illegal symbol
|
||||||
|
else
|
||||||
|
break; // return null token
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we got here, invalid token
|
||||||
|
T.type = TOKEN_INVALID;
|
||||||
|
return T;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Parser
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
BOOL CINIFile::Parse(void)
|
||||||
|
{
|
||||||
|
CToken T, U, V, W;
|
||||||
|
string currentSection; // current section we're processing
|
||||||
|
BOOL parseStatus = OKAY;
|
||||||
|
|
||||||
|
//currentSection = defaultSection; // default "global" section
|
||||||
|
lineNum = 0;
|
||||||
|
|
||||||
|
if (!File.is_open())
|
||||||
|
return FAIL;
|
||||||
|
File.clear();
|
||||||
|
if (!File.good())
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
while (!File.eof())
|
||||||
|
{
|
||||||
|
++lineNum;
|
||||||
|
File.getline(lineBuf,2048);
|
||||||
|
if (File.fail())
|
||||||
|
return FAIL;
|
||||||
|
linePtr = lineBuf; // beginning of line
|
||||||
|
|
||||||
|
// Top level
|
||||||
|
T = GetToken();
|
||||||
|
U = GetToken();
|
||||||
|
V = GetToken();
|
||||||
|
W = GetToken(); // should always be null
|
||||||
|
switch (T.type)
|
||||||
|
{
|
||||||
|
// [ Identifier ]
|
||||||
|
case '[':
|
||||||
|
if ((U.type==TOKEN_IDENTIFIER) && (V.type==']') && (W.type==TOKEN_NULL))
|
||||||
|
{
|
||||||
|
//printf("Section: %s\n", U.String.c_str());
|
||||||
|
currentSection = U.String;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
parseStatus = FAIL;
|
||||||
|
//printf("%d: parse error\n", lineNum);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Identifier '=' Argument
|
||||||
|
case TOKEN_IDENTIFIER:
|
||||||
|
if (U.type != '=')
|
||||||
|
{
|
||||||
|
parseStatus = FAIL;
|
||||||
|
//printf("%d: expected '=' after identifier\n", lineNum);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (((V.type==TOKEN_NUMBER) || (V.type==TOKEN_STRING)) && (W.type==TOKEN_NULL))
|
||||||
|
{
|
||||||
|
if (V.type == TOKEN_NUMBER)
|
||||||
|
{
|
||||||
|
//printf("\t%s = %X\n", T.String.c_str(), V.number);
|
||||||
|
Set(currentSection, T.String, V.number);
|
||||||
|
}
|
||||||
|
else if (V.type == TOKEN_STRING)
|
||||||
|
{
|
||||||
|
//printf("\t%s = %s\n", T.String.c_str(), V.String.c_str());
|
||||||
|
Set(currentSection, T.String, V.String);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
parseStatus = FAIL;
|
||||||
|
//printf("%d: expected a number or string after '='\n", lineNum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Blank line
|
||||||
|
case TOKEN_NULL:
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Illegal
|
||||||
|
default:
|
||||||
|
parseStatus = FAIL;
|
||||||
|
//printf("%d: parse error\n", lineNum);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//printf("end of file reached\n");
|
||||||
|
return parseStatus;
|
||||||
|
}
|
198
INIFile.h
Normal file
198
INIFile.h
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* INIFile.h
|
||||||
|
*
|
||||||
|
* Header file for INI file management.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDED_INIFILE_H
|
||||||
|
#define INCLUDED_INIFILE_H
|
||||||
|
|
||||||
|
// Standard C++ and STL headers
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CINIFile:
|
||||||
|
*
|
||||||
|
* INI file parser.
|
||||||
|
*/
|
||||||
|
class CINIFile
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
* Get(SectionName, SettingName, value):
|
||||||
|
* Get(SectionName, SettingName, String):
|
||||||
|
*
|
||||||
|
* Obtains the value of a setting associated with a particular section. It
|
||||||
|
* is assumed that the caller knows whether the setting should be a string
|
||||||
|
* or an integer value. If the setting was specified as a string in the
|
||||||
|
* INI file, the value will be set to 0. Otherwise, the string will be set
|
||||||
|
* to "".
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* SectionName String defining the section name.
|
||||||
|
* SettingName String defining the setting name.
|
||||||
|
* value Reference to where the setting value will be
|
||||||
|
* copied.
|
||||||
|
* String Reference to where the string will be copied.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* OKAY if the setting was found, FAIL otherwise. The type is not
|
||||||
|
* checked.
|
||||||
|
*/
|
||||||
|
BOOL Get(string SectionName, string SettingName, unsigned& value);
|
||||||
|
BOOL Get(string SectionName, string SettingName, string& String);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set(SectionName, SettingName, value):
|
||||||
|
* Set(SectionName, SettingName, String):
|
||||||
|
*
|
||||||
|
* Sets a setting associated with a particular section. For each overload
|
||||||
|
* type (string or integer), the opposite type is cleard (0 is written to
|
||||||
|
* values when a string is set, "" is written to strings when a value is
|
||||||
|
* set). If the setting does not exist, it is created. If the section does
|
||||||
|
* not exist, it will be added as well.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* SectionName String defining the section name.
|
||||||
|
* SettingName String defining the setting name.
|
||||||
|
* value Value to write. String will be set to "".
|
||||||
|
* String String to write. Value will be set to 0.
|
||||||
|
*/
|
||||||
|
void Set(string SectionName, string SettingName, unsigned value);
|
||||||
|
void Set(string SectionName, string SettingName, string String);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write(comment):
|
||||||
|
*
|
||||||
|
* Outputs the parse tree to the INI file. The current contents of the file
|
||||||
|
* on the disk are discarded and overwritten. This means that comments are
|
||||||
|
* lost.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* comment An optional C-string containing a comment to insert at
|
||||||
|
* the beginning of the file, otherwise NULL. The comment
|
||||||
|
* lines must include semicolons at the beginning, they
|
||||||
|
* will not be inserted automatically. The string is
|
||||||
|
* output directly, as-is.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* OKAY if successful. FAIL if an error occurred at any point. In
|
||||||
|
* order to truncate the original contents of the file, this function
|
||||||
|
* closes the file, reopends it as write-only, then reopens it again
|
||||||
|
* as a read/write file. If an error occurs during the reopening
|
||||||
|
* procedure, it is possible that nothing will be output and the
|
||||||
|
* previous contents will be lost.
|
||||||
|
*/
|
||||||
|
BOOL Write(const char *comment);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse(void):
|
||||||
|
*
|
||||||
|
* Parses the contents of the file, building the parse tree. Settings
|
||||||
|
* already present in the parse tree will continue to exist and will be
|
||||||
|
* overwritten if new, matching settings are found in the file.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
*/
|
||||||
|
BOOL Parse(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open(fileNameStr):
|
||||||
|
*
|
||||||
|
* Opens an INI file.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* fileNameStr File path (C string).
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* OKAY if successful, FAIL if unable to open for reading and writing.
|
||||||
|
*/
|
||||||
|
BOOL Open(const char *fileNameStr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Close(void):
|
||||||
|
*
|
||||||
|
* Closes the INI file and clears the parse tree.
|
||||||
|
*/
|
||||||
|
void Close(void);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Token
|
||||||
|
class CToken
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int type; // token type (defined privately in INIFile.cpp)
|
||||||
|
unsigned number; // numbers and bools
|
||||||
|
string String; // strings and identifiers
|
||||||
|
|
||||||
|
// Constructor (initialize to null token)
|
||||||
|
CToken(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Searching/modification of settings
|
||||||
|
BOOL LookUpSection(unsigned *idx, string SectionName);
|
||||||
|
BOOL LookUpSetting(unsigned *idx, unsigned sectionIdx, string SettingName);
|
||||||
|
|
||||||
|
// Tokenizer
|
||||||
|
CToken GetString(void);
|
||||||
|
CToken GetNumber(void);
|
||||||
|
CToken GetIdentifier(void);
|
||||||
|
CToken GetToken(void);
|
||||||
|
|
||||||
|
// File state
|
||||||
|
fstream File;
|
||||||
|
string FileName; // name of last file opened
|
||||||
|
|
||||||
|
// Parser state
|
||||||
|
char lineBuf[2048]; // holds current line
|
||||||
|
char *linePtr; // points to current position within line (for tokenization)
|
||||||
|
unsigned lineNum; // line number
|
||||||
|
|
||||||
|
// Parse tree: a list of sections each of which is a list of settings for that section
|
||||||
|
struct Setting // it is up to caller to determine whether to use value or string
|
||||||
|
{
|
||||||
|
string Name; // setting name
|
||||||
|
BOOL isNumber; // internal flag: true if the setting is a number, false if it is a string
|
||||||
|
unsigned value; // value of number
|
||||||
|
string String; // string
|
||||||
|
|
||||||
|
Setting(void)
|
||||||
|
{
|
||||||
|
value = 0; // initialize value to 0
|
||||||
|
isNumber = TRUE; // indicate the setting is initially a number
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct Section
|
||||||
|
{
|
||||||
|
string Name; // section name
|
||||||
|
vector<struct Setting> Settings; // list of settings associated w/ this section
|
||||||
|
};
|
||||||
|
vector<struct Section> Sections; // a list of sections
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // INCLUDED_INIFILE_H
|
105
Inputs/Input.cpp
Normal file
105
Inputs/Input.cpp
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
#include "Supermodel.h"
|
||||||
|
|
||||||
|
CInput::CInput(const char *inputId, const char *inputLabel, unsigned inputFlags, unsigned inputGameFlags, const char *defaultMapping, UINT16 initValue) :
|
||||||
|
id(inputId), label(inputLabel), flags(inputFlags), gameFlags(inputGameFlags), m_defaultMapping(defaultMapping), value(initValue), prevValue(initValue),
|
||||||
|
m_system(NULL), m_source(NULL)
|
||||||
|
{
|
||||||
|
ResetToDefaultMapping();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CInput::CreateSource()
|
||||||
|
{
|
||||||
|
// If no system set yet or mapping is empty or NONE, then set source to NULL
|
||||||
|
if (m_system == NULL || m_mapping[0] == '\0' || stricmp(m_mapping, "NONE") == 0)
|
||||||
|
m_source = NULL;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Otherwise, ask system to parse mapping into appropriate input source
|
||||||
|
m_source = m_system->ParseSource(m_mapping, !!(flags & INPUT_FLAGS_AXIS));
|
||||||
|
|
||||||
|
// Check that mapping was parsed okay and if not then fall back to default mapping
|
||||||
|
if (m_source == NULL && stricmp(m_mapping, m_defaultMapping) != 0)
|
||||||
|
{
|
||||||
|
ErrorLog("Unable to map input %s to [%s] - switching to default [%s].\n", id, m_mapping, m_defaultMapping);
|
||||||
|
|
||||||
|
ResetToDefaultMapping();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CInput::Initialize(CInputSystem *system)
|
||||||
|
{
|
||||||
|
m_system = system;
|
||||||
|
|
||||||
|
CreateSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* CInput::GetInputGroup()
|
||||||
|
{
|
||||||
|
switch (gameFlags)
|
||||||
|
{
|
||||||
|
case GAME_INPUT_COMMON: return "Common Controls";
|
||||||
|
case GAME_INPUT_JOYSTICK1: // Fall through to below
|
||||||
|
case GAME_INPUT_JOYSTICK2: return "8-Way Joysticks";
|
||||||
|
case GAME_INPUT_FIGHTING: return "Fighting Game Buttons";
|
||||||
|
case GAME_INPUT_SOCCER: return "Virtua Striker Buttons";
|
||||||
|
case GAME_INPUT_VEHICLE: return "Racing Game Steering Controls";
|
||||||
|
case GAME_INPUT_SHIFT4: return "Racing Game Gear Shift";
|
||||||
|
case GAME_INPUT_VR: return "Racing Game VR View Buttons";
|
||||||
|
case GAME_INPUT_RALLY: return "Sega Rally Buttons";
|
||||||
|
case GAME_INPUT_TWIN_JOYSTICKS: return "Virtua On Controls";
|
||||||
|
case GAME_INPUT_ANALOG_JOYSTICK: return "Analog Joystick";
|
||||||
|
case GAME_INPUT_GUN1: // Fall through to below
|
||||||
|
case GAME_INPUT_GUN2: return "Lightguns";
|
||||||
|
default: return "Misc";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *CInput::GetMapping()
|
||||||
|
{
|
||||||
|
return m_mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CInput::ClearMapping()
|
||||||
|
{
|
||||||
|
SetMapping("NONE");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CInput::SetMapping(const char *mapping)
|
||||||
|
{
|
||||||
|
strncpy(m_mapping, mapping, MAX_MAPPING_LENGTH - 1);
|
||||||
|
m_mapping[MAX_MAPPING_LENGTH - 1] = '\0';
|
||||||
|
CreateSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CInput::AppendMapping(const char *mapping)
|
||||||
|
{
|
||||||
|
// If mapping is empty or NONE, then simply set mapping
|
||||||
|
if (m_mapping[0] == '\0' || stricmp(m_mapping, "NONE") == 0)
|
||||||
|
SetMapping(mapping);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Otherwise, append to mapping string and recreate source from new mapping string
|
||||||
|
int size = MAX_MAPPING_LENGTH - strlen(m_mapping);
|
||||||
|
strncat(m_mapping, ",", size--);
|
||||||
|
strncat(m_mapping, mapping, size);
|
||||||
|
CreateSource();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CInput::ResetToDefaultMapping()
|
||||||
|
{
|
||||||
|
SetMapping(m_defaultMapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CInput::Configure(bool append, const char *escapeMapping)
|
||||||
|
{
|
||||||
|
char mapping[MAX_MAPPING_LENGTH];
|
||||||
|
if (!m_system->ReadMapping(mapping, MAX_MAPPING_LENGTH, !!(flags & INPUT_FLAGS_AXIS), READ_ALL, escapeMapping))
|
||||||
|
return false;
|
||||||
|
if (append)
|
||||||
|
AppendMapping(mapping);
|
||||||
|
else
|
||||||
|
SetMapping(mapping);
|
||||||
|
return true;
|
||||||
|
}
|
114
Inputs/Input.h
Normal file
114
Inputs/Input.h
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
#ifndef INCLUDED_INPUT_H
|
||||||
|
#define INCLUDED_INPUT_H
|
||||||
|
|
||||||
|
#include "Types.h"
|
||||||
|
|
||||||
|
class CInputSource;
|
||||||
|
class CInputSystem;
|
||||||
|
|
||||||
|
// Flags for inputs
|
||||||
|
#define INPUT_FLAGS_SWITCH 0x0001
|
||||||
|
#define INPUT_FLAGS_ANALOG 0x0002
|
||||||
|
#define INPUT_FLAGS_AXIS 0x0004
|
||||||
|
#define INPUT_FLAGS_VIRTUAL 0x0008
|
||||||
|
|
||||||
|
#define MAX_MAPPING_LENGTH 255
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Base class for any type of Model3 input control.
|
||||||
|
*/
|
||||||
|
class CInput
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// Current mapping(s) for input, eg JOY1_XAXIS_POS
|
||||||
|
char m_mapping[MAX_MAPPING_LENGTH];
|
||||||
|
|
||||||
|
// Default mapping for input
|
||||||
|
const char *m_defaultMapping;
|
||||||
|
|
||||||
|
// Assigned input system
|
||||||
|
CInputSystem *m_system;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Creates an input source using the current input system and assigns it to this input.
|
||||||
|
*/
|
||||||
|
void CreateSource();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Current input source
|
||||||
|
CInputSource *m_source;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Input identifier
|
||||||
|
const char *id;
|
||||||
|
|
||||||
|
// Input label
|
||||||
|
const char *label;
|
||||||
|
|
||||||
|
// Input flags
|
||||||
|
unsigned flags;
|
||||||
|
|
||||||
|
// Input game flags
|
||||||
|
unsigned gameFlags;
|
||||||
|
|
||||||
|
// Current input value
|
||||||
|
UINT16 value;
|
||||||
|
|
||||||
|
// Previous input value
|
||||||
|
UINT16 prevValue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Constructs an input with the given identifier, label, flags, game flags, default mapping and initial value.
|
||||||
|
*/
|
||||||
|
CInput(const char *inputId, const char *inputLabel, unsigned inputFlags, unsigned inputGameFlags,
|
||||||
|
const char *defaultMapping = "NONE", UINT16 initValue = 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initializes this input with the given input system. Must be called before any other methods are used.
|
||||||
|
*/
|
||||||
|
void Initialize(CInputSystem *system);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the name of the group of controls that this input belongs to.
|
||||||
|
*/
|
||||||
|
const char* GetInputGroup();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the current mapping(s) assigned to this input.
|
||||||
|
*/
|
||||||
|
const char* GetMapping();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clears the current mapping(s) assigned to this input.
|
||||||
|
*/
|
||||||
|
void ClearMapping();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sets the current mapping(s) assigned to this input. Multiple mapping assignments are comma-separated, eg KEY_RIGHT,JOY1_XAXIS_POS
|
||||||
|
*/
|
||||||
|
void SetMapping(const char *mapping);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Appends a mapping to this input to create a multiple mapping assignment.
|
||||||
|
*/
|
||||||
|
void AppendMapping(const char *mapping);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Resets the mapping(s) assigned to this input to the input's default.
|
||||||
|
*/
|
||||||
|
void ResetToDefaultMapping();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Configures the current mapping(s) assigned to this input by asking the user for input.
|
||||||
|
* If append is true, then the user's selected mapping is appended. Otherwise, it overwrites the existing mapping(s).
|
||||||
|
* escapeMapping holds the input mapping used to exit the configuration without applying the changes.
|
||||||
|
*/
|
||||||
|
bool Configure(bool append, const char *escapeMapping = "KEY_ESCAPE");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Polls (updates) this input, updating its value from the input source
|
||||||
|
*/
|
||||||
|
virtual void Poll() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // INCLUDED_INPUT_H
|
84
Inputs/InputSource.cpp
Normal file
84
Inputs/InputSource.cpp
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
#include "InputSource.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
CInputSource::CInputSource(ESourceType sourceType) : type(sourceType)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
int CInputSource::Clamp(int val, int minVal, int maxVal)
|
||||||
|
{
|
||||||
|
if (val > maxVal) return maxVal;
|
||||||
|
else if (val < minVal) return minVal;
|
||||||
|
else return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CInputSource::Scale(int val, int fromMinVal, int fromMaxVal, int toMinVal, int toMaxVal)
|
||||||
|
{
|
||||||
|
return Scale(val, fromMinVal, fromMinVal, fromMaxVal, toMinVal, toMinVal, toMaxVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
int CInputSource::Scale(int val, int fromMinVal, int fromOffVal, int fromMaxVal, int toMinVal, int toOffVal, int toMaxVal)
|
||||||
|
{
|
||||||
|
int fromRange;
|
||||||
|
double frac;
|
||||||
|
if (fromMaxVal > fromMinVal)
|
||||||
|
{
|
||||||
|
val = Clamp(val, fromMinVal, fromMaxVal);
|
||||||
|
if (val > fromOffVal)
|
||||||
|
{
|
||||||
|
fromRange = fromMaxVal - fromOffVal;
|
||||||
|
frac = (double)(val - fromOffVal) / fromRange;
|
||||||
|
}
|
||||||
|
else if (val < fromOffVal)
|
||||||
|
{
|
||||||
|
fromRange = fromOffVal - fromMinVal;
|
||||||
|
frac = (double)(val - fromOffVal) / fromRange;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return toOffVal;
|
||||||
|
}
|
||||||
|
else if (fromMinVal > fromMaxVal)
|
||||||
|
{
|
||||||
|
val = Clamp(val, fromMaxVal, fromMinVal);
|
||||||
|
if (val > fromOffVal)
|
||||||
|
{
|
||||||
|
fromRange = fromMinVal - fromOffVal;
|
||||||
|
frac = (double)(fromOffVal - val) / fromRange;
|
||||||
|
}
|
||||||
|
else if (val < fromOffVal)
|
||||||
|
{
|
||||||
|
fromRange = fromOffVal - fromMaxVal;
|
||||||
|
frac = (double)(fromOffVal - val) / fromRange;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return toOffVal;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return toOffVal;
|
||||||
|
double toRange;
|
||||||
|
if (toMaxVal > toMinVal)
|
||||||
|
{
|
||||||
|
if (frac >= 0)
|
||||||
|
toRange = (double)(toMaxVal - toOffVal);
|
||||||
|
else
|
||||||
|
toRange = (double)(toOffVal - toMinVal);
|
||||||
|
return toOffVal + (int)(toRange * frac);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (frac >= 0)
|
||||||
|
toRange = (double)(toOffVal - toMaxVal);
|
||||||
|
else
|
||||||
|
toRange = (double)(toMinVal - toOffVal);
|
||||||
|
return toOffVal - (int)(toRange * frac);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CInputSource::IsActive()
|
||||||
|
{
|
||||||
|
bool boolVal;
|
||||||
|
return GetValueAsSwitch(boolVal);
|
||||||
|
}
|
72
Inputs/InputSource.h
Normal file
72
Inputs/InputSource.h
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
#ifndef INCLUDED_INPUTSOURCE_H
|
||||||
|
#define INCLUDED_INPUTSOURCE_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
class CInputSystem;
|
||||||
|
|
||||||
|
enum ESourceType
|
||||||
|
{
|
||||||
|
SourceInvalid = -1,
|
||||||
|
SourceEmpty = 0,
|
||||||
|
SourceSwitch = 1,
|
||||||
|
SourceHalfAxis = 2,
|
||||||
|
SourceFullAxis = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Provides values to inputs, either in the form of a bool for switch inputs or an int value within a given range for analog/axis inputs.
|
||||||
|
*/
|
||||||
|
class CInputSource
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
CInputSource(ESourceType sourceType);
|
||||||
|
|
||||||
|
public:
|
||||||
|
const ESourceType type;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Static helper methods
|
||||||
|
//
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clamps the given value to between the min and max values.
|
||||||
|
*/
|
||||||
|
static int Clamp(int val, int minVal, int maxVal);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Scales the given value, that falls within the stated 'from' range, to be between the given 'to' range.
|
||||||
|
*/
|
||||||
|
static int Scale(int val, int fromMinVal, int fromMaxVal, int toMinVal, int toMaxVal);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Scales the given value, that falls within the stated 'from' range, to be between the given 'to' range.
|
||||||
|
* The off values are supplied to avoid rounding errors and ensure that the scaled value has the correct off value when required.
|
||||||
|
*/
|
||||||
|
static int Scale(int val, int fromMinVal, int fromOffVal, int fromMaxVal, int toMinVal, int toOffVal, int toMaxVal);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if this source represents a switch input (such as a button). Otherwise it is assumed to be an analog input (such as axis or pedal).
|
||||||
|
*/
|
||||||
|
ESourceType GetType();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if the source is active (taken from GetValueAsSwitch).
|
||||||
|
*/
|
||||||
|
bool IsActive();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reads a boolean value for a switch input.
|
||||||
|
* Returns true if the value was set.
|
||||||
|
*/
|
||||||
|
virtual bool GetValueAsSwitch(bool &val) = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reads an int value for an analog/axis input, guaranteeing that the value falls within the given range and has the correct off value when required.
|
||||||
|
* Returns true if the value was set.
|
||||||
|
*/
|
||||||
|
virtual bool GetValueAsAnalog(int &val, int minVal, int offVal, int maxVal) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // INCLUDED_INPUTSOURCE_H
|
1899
Inputs/InputSystem.cpp
Normal file
1899
Inputs/InputSystem.cpp
Normal file
File diff suppressed because it is too large
Load diff
925
Inputs/InputSystem.h
Normal file
925
Inputs/InputSystem.h
Normal file
|
@ -0,0 +1,925 @@
|
||||||
|
#ifndef INCLUDED_INPUTSYSTEM_H
|
||||||
|
#define INCLUDED_INPUTSYSTEM_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#include "MultiInputSource.h"
|
||||||
|
|
||||||
|
class CInput;
|
||||||
|
class CInputSource;
|
||||||
|
class CINIFile;
|
||||||
|
|
||||||
|
// Read flags for ReadMapping
|
||||||
|
#define READ_KEYBOARD 1
|
||||||
|
#define READ_MOUSE 2
|
||||||
|
#define READ_JOYSTICK 4
|
||||||
|
#define READ_MERGE_KEYBOARD 8
|
||||||
|
#define READ_MERGE_MOUSE 16
|
||||||
|
#define READ_MERGE_JOYSTICK 32
|
||||||
|
#define READ_ALL (READ_KEYBOARD|READ_MOUSE|READ_JOYSTICK)
|
||||||
|
#define READ_MERGE (READ_MERGE_KEYBOARD|READ_MERGE_MOUSE|READ_MERGE_JOYSTICK)
|
||||||
|
|
||||||
|
// Used to specify any keyboard, mouse or joystick when a number is required
|
||||||
|
#define ANY_KEYBOARD -1
|
||||||
|
#define ANY_MOUSE -1
|
||||||
|
#define ANY_JOYSTICK -1
|
||||||
|
|
||||||
|
// Default keyboard, mouse and joystick settings
|
||||||
|
#define DEFAULT_KEY_SENSITIVITY 25
|
||||||
|
#define DEFAULT_KEY_DECAYSPEED 50
|
||||||
|
#define DEFAULT_MSE_DEADZONE 0
|
||||||
|
#define DEFAULT_JOY_DEADZONE 3
|
||||||
|
#define DEFAULT_JOY_SATURATION 100
|
||||||
|
|
||||||
|
// Number of valid keys and mouse and joystick parts
|
||||||
|
#define NUM_VALID_KEYS (sizeof(s_validKeyNames) / sizeof(const char*))
|
||||||
|
#define NUM_MOUSE_PARTS ((int)MouseButtonX2 + 1)
|
||||||
|
#define NUM_MOUSE_AXES 3
|
||||||
|
#define NUM_MOUSE_BUTTONS 5
|
||||||
|
#define NUM_JOY_PARTS ((int)JoyButton31 + 1)
|
||||||
|
#define NUM_JOY_AXES 6
|
||||||
|
#define NUM_JOY_POVS 4
|
||||||
|
#define NUM_JOY_BUTTONS 32
|
||||||
|
|
||||||
|
// Axis numbers
|
||||||
|
#define AXIS_X 0
|
||||||
|
#define AXIS_Y 1
|
||||||
|
#define AXIS_Z 2
|
||||||
|
#define AXIS_RX 3
|
||||||
|
#define AXIS_RY 4
|
||||||
|
#define AXIS_RZ 5
|
||||||
|
|
||||||
|
// Axis directions
|
||||||
|
#define AXIS_FULL 0
|
||||||
|
#define AXIS_INVERTED 1
|
||||||
|
#define AXIS_POS 2
|
||||||
|
#define AXIS_NEG 3
|
||||||
|
|
||||||
|
// POV directions
|
||||||
|
#define POV_UP 0
|
||||||
|
#define POV_DOWN 1
|
||||||
|
#define POV_LEFT 2
|
||||||
|
#define POV_RIGHT 3
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enumeration of all recognised mouse parts
|
||||||
|
*/
|
||||||
|
enum EMousePart
|
||||||
|
{
|
||||||
|
MouseUnknown = -1,
|
||||||
|
MouseXAxis = 0,
|
||||||
|
MouseXAxisInv,
|
||||||
|
MouseXAxisPos,
|
||||||
|
MouseXAxisNeg,
|
||||||
|
MouseYAxis,
|
||||||
|
MouseYAxisInv,
|
||||||
|
MouseYAxisPos,
|
||||||
|
MouseYAxisNeg,
|
||||||
|
MouseZAxis,
|
||||||
|
MouseZAxisInv,
|
||||||
|
MouseZAxisPos,
|
||||||
|
MouseZAxisNeg,
|
||||||
|
MouseButtonLeft,
|
||||||
|
MouseButtonMiddle,
|
||||||
|
MouseButtonRight,
|
||||||
|
MouseButtonX1,
|
||||||
|
MouseButtonX2,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enumeration of all supported joystick parts
|
||||||
|
*/
|
||||||
|
enum EJoyPart
|
||||||
|
{
|
||||||
|
JoyUnknown = -1,
|
||||||
|
JoyXAxis = 0,
|
||||||
|
JoyXAxisInv,
|
||||||
|
JoyXAxisPos,
|
||||||
|
JoyXAxisNeg,
|
||||||
|
JoyYAxis,
|
||||||
|
JoyYAxisInv,
|
||||||
|
JoyYAxisPos,
|
||||||
|
JoyYAxisNeg,
|
||||||
|
JoyZAxis,
|
||||||
|
JoyZAxisInv,
|
||||||
|
JoyZAxisPos,
|
||||||
|
JoyZAxisNeg,
|
||||||
|
JoyRXAxis,
|
||||||
|
JoyRXAxisInv,
|
||||||
|
JoyRXAxisPos,
|
||||||
|
JoyRXAxisNeg,
|
||||||
|
JoyRYAxis,
|
||||||
|
JoyRYAxisInv,
|
||||||
|
JoyRYAxisPos,
|
||||||
|
JoyRYAxisNeg,
|
||||||
|
JoyRZAxis,
|
||||||
|
JoyRZAxisInv,
|
||||||
|
JoyRZAxisPos,
|
||||||
|
JoyRZAxisNeg,
|
||||||
|
JoyPOV0Up,
|
||||||
|
JoyPOV0Down,
|
||||||
|
JoyPOV0Left,
|
||||||
|
JoyPOV0Right,
|
||||||
|
JoyPOV1Up,
|
||||||
|
JoyPOV1Down,
|
||||||
|
JoyPOV1Left,
|
||||||
|
JoyPOV1Right,
|
||||||
|
JoyPOV2Up,
|
||||||
|
JoyPOV2Down,
|
||||||
|
JoyPOV2Left,
|
||||||
|
JoyPOV2Right,
|
||||||
|
JoyPOV3Up,
|
||||||
|
JoyPOV3Down,
|
||||||
|
JoyPOV3Left,
|
||||||
|
JoyPOV3Right,
|
||||||
|
JoyButton0,
|
||||||
|
JoyButton1,
|
||||||
|
JoyButton2,
|
||||||
|
JoyButton3,
|
||||||
|
JoyButton4,
|
||||||
|
JoyButton5,
|
||||||
|
JoyButton6,
|
||||||
|
JoyButton7,
|
||||||
|
JoyButton8,
|
||||||
|
JoyButton9,
|
||||||
|
JoyButton10,
|
||||||
|
JoyButton11,
|
||||||
|
JoyButton12,
|
||||||
|
JoyButton13,
|
||||||
|
JoyButton14,
|
||||||
|
JoyButton15,
|
||||||
|
JoyButton16,
|
||||||
|
JoyButton17,
|
||||||
|
JoyButton18,
|
||||||
|
JoyButton19,
|
||||||
|
JoyButton20,
|
||||||
|
JoyButton21,
|
||||||
|
JoyButton22,
|
||||||
|
JoyButton23,
|
||||||
|
JoyButton24,
|
||||||
|
JoyButton25,
|
||||||
|
JoyButton26,
|
||||||
|
JoyButton27,
|
||||||
|
JoyButton28,
|
||||||
|
JoyButton29,
|
||||||
|
JoyButton30,
|
||||||
|
JoyButton31
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MousePartsStruct
|
||||||
|
{
|
||||||
|
const char* id;
|
||||||
|
EMousePart msePart;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct JoyPartsStruct
|
||||||
|
{
|
||||||
|
const char* id;
|
||||||
|
EJoyPart joyPart;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Struct that holds settings for a keyboard
|
||||||
|
*/
|
||||||
|
struct KeySettings
|
||||||
|
{
|
||||||
|
int kbdNum; // Keyboard number (or ANY_KEYBOARD for settings that apply to all keyboards)
|
||||||
|
unsigned sensitivity; // Key sensitivity for analog controls as percentage 1-100, where 100 is extremely responsive
|
||||||
|
unsigned decaySpeed; // Decay speed as percentage 1-200 of on speed
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Creates a KeySettings with default settings
|
||||||
|
*/
|
||||||
|
KeySettings()
|
||||||
|
{
|
||||||
|
kbdNum = ANY_KEYBOARD;
|
||||||
|
sensitivity = DEFAULT_KEY_SENSITIVITY;
|
||||||
|
decaySpeed = DEFAULT_KEY_DECAYSPEED;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Struct that holds settings for a mouse
|
||||||
|
*/
|
||||||
|
struct MouseSettings
|
||||||
|
{
|
||||||
|
int mseNum; // Mouse number (or ANY_MOUSE for settings that apply to all mice)
|
||||||
|
unsigned xDeadZone; // X-Axis dead zone as a percentage 0-99 of display width
|
||||||
|
unsigned yDeadZone; // Y-Axis dead zone as a percentage 0-99 of display height
|
||||||
|
unsigned zDeadZone; // Z-Axis dead zone as a percentage 0-99 of axis range
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Creates a MouseSettings with default settings
|
||||||
|
*/
|
||||||
|
MouseSettings()
|
||||||
|
{
|
||||||
|
mseNum = ANY_MOUSE;
|
||||||
|
xDeadZone = DEFAULT_MSE_DEADZONE;
|
||||||
|
yDeadZone = DEFAULT_MSE_DEADZONE;
|
||||||
|
zDeadZone = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Struct that holds settings for a joystick
|
||||||
|
*/
|
||||||
|
struct JoySettings
|
||||||
|
{
|
||||||
|
int joyNum; // Joystick number (or ANY_JOYSTICK for settings that apply to all joysticks)
|
||||||
|
unsigned xDeadZone; // X-Axis dead zone as a percentage 0-99 of axis positive/negative ranges
|
||||||
|
unsigned xSaturation; // X-Axis saturation as a percentage 1-100 of axis positive/negative ranges
|
||||||
|
unsigned yDeadZone; // Y-Axis dead zone as a percentage 0-99 of axis positive/negative ranges
|
||||||
|
unsigned ySaturation; // Y-Axis saturation as a percentage 1-100 of axis positive/negative ranges
|
||||||
|
unsigned zDeadZone; // Z-Axis dead zone as a percentage 0-99 of axis positive/negative ranges
|
||||||
|
unsigned zSaturation; // Z-Axis saturation as a percentage 1-100 of axis positive/negative ranges
|
||||||
|
unsigned rxDeadZone; // RX-Axis dead zone as a percentage 0-99 of axis positive/negative ranges
|
||||||
|
unsigned rxSaturation; // RX-Axis saturation as a percentage 1-100 of joystick axis positive/negative ranges
|
||||||
|
unsigned ryDeadZone; // RY-Axis dead zone as a percentage 0-99 of axis positive/negative ranges
|
||||||
|
unsigned rySaturation; // RY-Axis saturation as a percentage 1-100 of joystick axis positive/negative ranges
|
||||||
|
unsigned rzDeadZone; // RZ-Axis dead zone as a percentage 0-99 of axis positive/negative ranges
|
||||||
|
unsigned rzSaturation; // RZ-Axis saturation as a percentage 1-100 of joystick axis positive/negative ranges
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Creates a JoySettings with default settings
|
||||||
|
*/
|
||||||
|
JoySettings()
|
||||||
|
{
|
||||||
|
joyNum = ANY_JOYSTICK;
|
||||||
|
xDeadZone = DEFAULT_JOY_DEADZONE;
|
||||||
|
xSaturation = DEFAULT_JOY_SATURATION;
|
||||||
|
yDeadZone = DEFAULT_JOY_DEADZONE;
|
||||||
|
ySaturation = DEFAULT_JOY_SATURATION;
|
||||||
|
zDeadZone = DEFAULT_JOY_DEADZONE;
|
||||||
|
zSaturation = DEFAULT_JOY_SATURATION;
|
||||||
|
rxDeadZone = DEFAULT_JOY_DEADZONE;
|
||||||
|
rxSaturation = DEFAULT_JOY_SATURATION;
|
||||||
|
ryDeadZone = DEFAULT_JOY_DEADZONE;
|
||||||
|
rySaturation = DEFAULT_JOY_SATURATION;
|
||||||
|
rzDeadZone = DEFAULT_JOY_DEADZONE;
|
||||||
|
rzSaturation = DEFAULT_JOY_SATURATION;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct JoyDetails
|
||||||
|
{
|
||||||
|
bool hasXAxis; // Flags to indicate which axes available on joystick
|
||||||
|
bool hasYAxis;
|
||||||
|
bool hasZAxis;
|
||||||
|
bool hasRXAxis;
|
||||||
|
bool hasRYAxis;
|
||||||
|
bool hasRZAxis;
|
||||||
|
int numAxes; // Total number of axes on joystick
|
||||||
|
int numPOVs; // Total number of POV hat controllers on joystick
|
||||||
|
int numButtons; // Total number of buttons on joystick
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Abstract base class that represents an input system. An input system encapsulates all the O/S dependent code to read keyboards,
|
||||||
|
* mice and joysticks.
|
||||||
|
*/
|
||||||
|
class CInputSystem
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// Array of valid key names
|
||||||
|
static const char *s_validKeyNames[];
|
||||||
|
|
||||||
|
// Lookup table for translating mouse mapping strings to their respective mouse parts
|
||||||
|
static MousePartsStruct s_mseParts[];
|
||||||
|
|
||||||
|
// Lookup table for translating joystick mapping strings to their respective joystick parts
|
||||||
|
static JoyPartsStruct s_joyParts[];
|
||||||
|
|
||||||
|
// Empty input source
|
||||||
|
static CMultiInputSource *s_emptySource;
|
||||||
|
|
||||||
|
// Number of keyboards, mice and joysticks
|
||||||
|
int m_numKbds;
|
||||||
|
int m_numMice;
|
||||||
|
int m_numJoys;
|
||||||
|
|
||||||
|
// Cached input sources
|
||||||
|
CInputSource **m_anyKeySources;
|
||||||
|
CInputSource **m_anyMseSources;
|
||||||
|
CInputSource **m_anyJoySources;
|
||||||
|
CInputSource ***m_keySources;
|
||||||
|
CInputSource ***m_mseSources;
|
||||||
|
CInputSource ***m_joySources;
|
||||||
|
|
||||||
|
// Default key, mouse and joystick settings
|
||||||
|
KeySettings m_defKeySettings;
|
||||||
|
MouseSettings m_defMseSettings;
|
||||||
|
JoySettings m_defJoySettings;
|
||||||
|
|
||||||
|
// Current key, mouse and joystick settings for attached keyboards, mice and joysticks
|
||||||
|
vector<KeySettings*> m_keySettings;
|
||||||
|
vector<MouseSettings*> m_mseSettings;
|
||||||
|
vector<JoySettings*> m_joySettings;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Helper methods
|
||||||
|
//
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Creates cache for all sources.
|
||||||
|
*/
|
||||||
|
void CreateSourceCache();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Deletes cache for all sources.
|
||||||
|
*/
|
||||||
|
void DeleteSourceCache();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Deletes an input source
|
||||||
|
*/
|
||||||
|
void DeleteSource(CInputSource *source);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns a key source for the given keyboard number (or all keyboards if ANY_KEYBOARD supplied) and key index
|
||||||
|
*/
|
||||||
|
CInputSource *GetKeySource(int kbdNum, int keyIndex);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns a mouse source for the given mouse number (or all mice if ANY_MOUSE supplied) and mouse index
|
||||||
|
*/
|
||||||
|
CInputSource *GetMouseSource(int mseNum, EMousePart msePart);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns a joystick source for the given joystick number (or all joysticks if ANY_JOYSTICK supplied) and joystick index
|
||||||
|
*/
|
||||||
|
CInputSource *GetJoySource(int joyNum, EJoyPart joyPart);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Finds any currently activated key sources for the given keyboard number (or all keyboards if ANY_KEYBOARD supplied)
|
||||||
|
* and adds them to the sources vector, aswell as constructing the corresponding mapping(s) in the given string.
|
||||||
|
* If fullAxisOnly is true, then only sources that represent a full axis range (eg MouseXAxis) are considered.
|
||||||
|
*/
|
||||||
|
void CheckKeySources(int kbdNum, bool fullAxisOnly, vector<CInputSource*> &sources, string &mapping);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Finds any currently activated mouse sources for the given mouse number (or all mice if ANY_MOUSE supplied)
|
||||||
|
* and adds them to the sources vector, aswell as constructing the corresponding mapping(s) in the given string.
|
||||||
|
* If fullAxisOnly is true, then only sources that represent a full axis range (eg MouseXAxis) are considered.
|
||||||
|
*/
|
||||||
|
void CheckMouseSources(int mseNum, bool fullAxisOnly, bool mseCentered, vector<CInputSource*> &sources, string &mapping);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Finds any currently activated joystick sources for the given joystick number (or all joysticks if ANY_JOYSTICK supplied)
|
||||||
|
* and adds them to the sources vector, aswell as constructing the corresponding mapping(s) in the given string.
|
||||||
|
* If fullAxisOnly is true, then only sources that represent a full axis range (eg MouseXAxis) are considered.
|
||||||
|
*/
|
||||||
|
void CheckJoySources(int joyNum, bool fullAxisOnly, vector<CInputSource*> &sources, string &mapping);
|
||||||
|
|
||||||
|
bool ParseInt(string str, int &num);
|
||||||
|
|
||||||
|
string IntToString(int num);
|
||||||
|
|
||||||
|
bool EqualsIgnoreCase(string str1, const char *str2);
|
||||||
|
|
||||||
|
bool StartsWithIgnoreCase(string str1, const char *str2);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if the given string represents a valid key name.
|
||||||
|
*/
|
||||||
|
bool IsValidKeyName(string str);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the EMousePart with the given mapping name or MouseUnknown if not found.
|
||||||
|
*/
|
||||||
|
EMousePart LookupMousePart(string str);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the mapping name for the given EMousePart.
|
||||||
|
*/
|
||||||
|
const char *LookupName(EMousePart msePart);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the EJoyPart with the given mapping name or JoyUnknown if not found.
|
||||||
|
*/
|
||||||
|
EJoyPart LookupJoyPart(string str);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the mapping name for the given EJoyPart.
|
||||||
|
*/
|
||||||
|
const char *LookupName(EJoyPart joyPart);
|
||||||
|
|
||||||
|
size_t ParseDevMapping(string str, const char *devType, int &devNum);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parses the given mapping string, possibly representing more than one mapping, and returns an input source for it or NULL if the
|
||||||
|
* mapping is invalid.
|
||||||
|
* If fullAxisOnly is true, then only mappings that represent a full axis range (eg MouseXAxis) are parsed.
|
||||||
|
*/
|
||||||
|
CInputSource* ParseMultiSource(string str, bool fullAxisOnly, bool isOr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parses the given single mapping string and returns an input source for it, or NULL if non exists.
|
||||||
|
*/
|
||||||
|
CInputSource* ParseSingleSource(string str);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prints the given key settings to stdout.
|
||||||
|
*/
|
||||||
|
void PrintKeySettings(KeySettings *settings);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reads key settings from an INI file for the given keyboard number, or common settings if ANY_KEYBOARD specified.
|
||||||
|
* Returns NULL if no relevant settings were found in the INI file.
|
||||||
|
*/
|
||||||
|
KeySettings *ReadKeySettings(CINIFile *ini, const char *section, int kbdNum);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Writes the given key settings to an INI file, only writing out settings that are different to their defaults.
|
||||||
|
*/
|
||||||
|
void WriteKeySettings(CINIFile *ini, const char *section, KeySettings *settings);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prints the given mouse settings to stdout.
|
||||||
|
*/
|
||||||
|
void PrintMouseSettings(MouseSettings *settings);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reads mouse settings from an INI file for the given mouse number, or common settings if ANY_MOUSE specified.
|
||||||
|
* Returns NULL if no relevant settings were found in the INI file.
|
||||||
|
*/
|
||||||
|
MouseSettings *ReadMouseSettings(CINIFile *ini, const char *section, int mseNum);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Writes the given mouse settings to an INI file, only writing out settings that are different to their defaults.
|
||||||
|
*/
|
||||||
|
void WriteMouseSettings(CINIFile *ini, const char *section, MouseSettings *settings);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prints the given joystick settings to stdout.
|
||||||
|
*/
|
||||||
|
void PrintJoySettings(JoySettings *settings);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reads joystick settings from an INI file for the given joystick number, or common settings if ANY_JOYSTICK specified.
|
||||||
|
* Returns NULL if no relevant settings were found in the INI file.
|
||||||
|
*/
|
||||||
|
JoySettings *ReadJoySettings(CINIFile *ini, const char *section, int joyNum);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Writes the given joystick settings to an INI file, only writing out settings that are different to their defaults.
|
||||||
|
*/
|
||||||
|
void WriteJoySettings(CINIFile *ini, const char *section, JoySettings *settings);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Current display geometry
|
||||||
|
unsigned m_dispX;
|
||||||
|
unsigned m_dispY;
|
||||||
|
unsigned m_dispW;
|
||||||
|
unsigned m_dispH;
|
||||||
|
|
||||||
|
// Flag to indicate if system currently being configured
|
||||||
|
bool m_isConfiguring;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Constructs an input system with the given name.
|
||||||
|
*/
|
||||||
|
CInputSystem(const char *systemName);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the current key settings for given keyboard number, or common settings if ANY_KEYBOARD specified.
|
||||||
|
* If no settings are found and useDefault is false, NULL is returned. If useDefault is true then default settings are returned.
|
||||||
|
*/
|
||||||
|
KeySettings *GetKeySettings(int kbdNum, bool useDefault);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the current mouse settings for given mouse number, or common settings if ANY_MOUSE specified.
|
||||||
|
* If no settings are found and useDefault is false, NULL is returned. If useDefault is true then default settings are returned.
|
||||||
|
*/
|
||||||
|
MouseSettings *GetMouseSettings(int mseNum, bool useDefault);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the current joystick settings for given joystick number, or common settings if ANY_JOYSTICK specified.
|
||||||
|
* If no settings are found and useDefault is false, NULL is returned. If useDefault is true then default settings are returned.
|
||||||
|
*/
|
||||||
|
JoySettings *GetJoySettings(int joyNum, bool useDefault);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if the given EMousePart is an axis.
|
||||||
|
*/
|
||||||
|
bool IsAxis(EMousePart msePart);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if the given EMousePart represents a full axis, eg MouseXAxis or MouseXAxisInv.
|
||||||
|
*/
|
||||||
|
bool IsFullAxis(EMousePart msePart);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if the EMousePart represents an axis and sets axisPart and axisDir as follows:
|
||||||
|
* axisNum will be the axis number (AXIS_X, AXIS_Y or AXIS_Z)
|
||||||
|
* axisDir will be AXIS_FULL, AXIS_INVERTED, AXIS_POS or AXIS_POS depending on whether the axis has the full range, has
|
||||||
|
* the full range but inverted, is negative only, or is positive only.
|
||||||
|
*/
|
||||||
|
bool GetAxisDetails(EMousePart msePart, int &axisNum, int &axisDir);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if the given EMousePart represets a button, eg MouseButtonLeft.
|
||||||
|
*/
|
||||||
|
bool IsButton(EMousePart msePart);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the button number (indexed from 0) of the given EMousePart if it is a button, or -1 otherwise.
|
||||||
|
*/
|
||||||
|
int GetButtonNumber(EMousePart msePart);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the EMousePart that represents the axis component for the given axis number (AXIS_X, AXIS_Y or AXIS_Z) and direction
|
||||||
|
* (AXIS_FULL, AXIS_INVERTED, AXIS_POS or AXIS_POS), or MouseUnknown otherwise.
|
||||||
|
*/
|
||||||
|
EMousePart GetMouseAxis(int axisNum, int axisDir);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the EMousePart that represents the mouse button with the given number (0-4), or MouseUnknown otherwise.
|
||||||
|
*/
|
||||||
|
EMousePart GetMouseButton(int butNum);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if the given EJoyPart is an axis.
|
||||||
|
*/
|
||||||
|
bool IsAxis(EJoyPart joyPart);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if the given EJoyPart represents a full axis, eg JoyXAxis.
|
||||||
|
*/
|
||||||
|
bool IsFullAxis(EJoyPart joyPart);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if joystick part represents an axis and sets axisPart and axisDir as follows:
|
||||||
|
* axisNum will be the axis number (AXIS_X, AXIS_Y, AXIS_Z, AXIS_RX, AXIS_RY or AXIS_RZ)
|
||||||
|
* axisDir is AXIS_FULL, AXIS_INVERTED, AXIS_POS or AXIS_POS depending on whether the axis has the full range, has
|
||||||
|
* the full range but inverted, is negative only, or is positive only.
|
||||||
|
*/
|
||||||
|
bool GetAxisDetails(EJoyPart joyPart, int &axisNum, int &axisDir);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if the given EJoyPart represents a POV hat direction, eg JoyPOV0Left.
|
||||||
|
*/
|
||||||
|
bool IsPOV(EJoyPart joyPart);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if the EJoyPart represents a POV hat direction and sets povNum and povDir as follows:
|
||||||
|
* povNum will be the POV hat number 0-4,
|
||||||
|
* povDir will be POV_UP, POV_DOWN, POV_LEFT or POV_RIGHT.
|
||||||
|
*/
|
||||||
|
bool GetPOVDetails(EJoyPart joyPart, int &povNum, int &povDir);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if the given EJoyPart is a button
|
||||||
|
*/
|
||||||
|
bool IsButton(EJoyPart joyPart);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the button number (indexed from 0) of the given EJoyPart if it is a button, or -1 otherwise.
|
||||||
|
*/
|
||||||
|
int GetButtonNumber(EJoyPart joyPart);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the EJoyPart that represents the axis component for the given axis number (AXIS_X, AXIS_Y, AXIS_Z, AXIS_RX, AXIS_RY or AXIS_RZ) and
|
||||||
|
* direction (AXIS_FULL, AXIS_INVERTED, AXIS_POS or AXIS_POS), or JoyUnknown otherwise.
|
||||||
|
*/
|
||||||
|
EJoyPart GetJoyAxis(int axisNum, int axisDir);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the EJoyPart that represents the POV hot direction for the given POV number (0-4) and direction (POV_UP, POV_DOWN,
|
||||||
|
* POV_LEFT or POV_RIGHT), JoyUnknown otherwise.
|
||||||
|
*/
|
||||||
|
EJoyPart GetJoyPOV(int povNum, int povDir);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the EJoyPart that represents the joystick button with the given number (0-31), or JoyUnknown otherwise.
|
||||||
|
*/
|
||||||
|
EJoyPart GetJoyButton(int butNum);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Abstract methods subclass must implement (ie system-specific code)
|
||||||
|
//
|
||||||
|
|
||||||
|
virtual bool InitializeSystem() = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the number of attached keyboards (or 0 if the system cannot handle keyboards at all or ANY_KEYBOARD if the system cannot
|
||||||
|
* handle multiple keyboards).
|
||||||
|
*/
|
||||||
|
virtual int GetNumKeyboards() = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the number of attached mice (or 0 if the system cannot handle mice at all or ANY_MOUSE if the system cannot handle
|
||||||
|
* multiple mice).
|
||||||
|
*/
|
||||||
|
virtual int GetNumMice() = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns number of attached joysticks (or 0 if the system cannot handle joysticks at all or ANY_JOYSTICK if the system cannot
|
||||||
|
* handle multiple joysticks).
|
||||||
|
*/
|
||||||
|
virtual int GetNumJoysticks() = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the system-specific key index that represents the given key name.
|
||||||
|
*/
|
||||||
|
virtual int GetKeyIndex(const char *keyName) = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the key name of the given system-specific integer key index.
|
||||||
|
*/
|
||||||
|
virtual const char *GetKeyName(int keyIndex) = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns details about the joystick with the given number, or NULL if it does not exist.
|
||||||
|
*/
|
||||||
|
virtual JoyDetails *GetJoyDetails(int joyNum) = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if for the given keyboard the key with the system-specific key index is currently pressed.
|
||||||
|
*/
|
||||||
|
virtual bool IsKeyPressed(int kbdNum, int keyIndex) = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the current axis value for the given mouse and axis number (AXIS_X, AXIS_Y or AXIS_Z).
|
||||||
|
*/
|
||||||
|
virtual int GetMouseAxisValue(int mseNum, int axisNum) = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the current direction (-1, 0 or 1) the Z-axis (wheel) is moving in for the given mouse.
|
||||||
|
*/
|
||||||
|
virtual int GetMouseWheelDir(int mseNum) = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if for the given mouse the button with the given number is currently pressed.
|
||||||
|
*/
|
||||||
|
virtual bool IsMouseButPressed(int mseNum, int butNum) = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the current axis value for the given joystick and axis number (AXIS_X, AXIS_Y, AXIS_Z, AXIS_RX, AXIS_RY or AXIS_RZ).
|
||||||
|
*/
|
||||||
|
virtual int GetJoyAxisValue(int joyNum, int axisNum) = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if for the given joystick the POV-hat controller with the given number is pointing in a particular direction (POV_UP, POV_DOWN,
|
||||||
|
* POV_LEFT or POV_RIGHT)
|
||||||
|
*/
|
||||||
|
virtual bool IsJoyPOVInDir(int joyNum, int povNum, int povDir) = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if for the given joystick the button with the given number is currently pressed.
|
||||||
|
*/
|
||||||
|
virtual bool IsJoyButPressed(int joyNum, int butNum) = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Waits for the given time in milliseconds
|
||||||
|
*/
|
||||||
|
virtual void Wait(int ms) = 0;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Virtual methods subclass can override if required
|
||||||
|
//
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if the mouse is currently centered in the display.
|
||||||
|
*/
|
||||||
|
virtual bool ConfigMouseCentered();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Creates an input source combining all keyboards for the given key index.
|
||||||
|
*/
|
||||||
|
virtual CInputSource *CreateAnyKeySource(int keyIndex);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Creates an input source combining all mice for the given EMousePart.
|
||||||
|
*/
|
||||||
|
virtual CInputSource *CreateAnyMouseSource(EMousePart msePart);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Creates an input source combining all joysticks for the given EJoyPart.
|
||||||
|
*/
|
||||||
|
virtual CInputSource *CreateAnyJoySource(EJoyPart joyPart);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Creates an input source for the given keyboard number and key index.
|
||||||
|
*/
|
||||||
|
virtual CInputSource *CreateKeySource(int kbdNum, int keyIndex);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Creates an input source for the given mouse number and EMousePart.
|
||||||
|
*/
|
||||||
|
virtual CInputSource *CreateMouseSource(int mseNum, EMousePart msePart);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Creates an input source for the given joystick number and EJoyPart.
|
||||||
|
*/
|
||||||
|
virtual CInputSource *CreateJoySource(int joyNum, EJoyPart joyPart);
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Name of this input system
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
virtual ~CInputSystem();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initializes the input system. Must be called before any other methods are used.
|
||||||
|
* Returns false if unable to initialize the system.
|
||||||
|
*/
|
||||||
|
bool Initialize();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sets the current display geometry so that mouse movements can be scaled properly.
|
||||||
|
*/
|
||||||
|
void SetDisplayGeom(unsigned dispX, unsigned dispY, unsigned dispW, unsigned dispH);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the input source for the given mapping, or NULL if mapping is not valid.
|
||||||
|
*/
|
||||||
|
CInputSource* ParseSource(const char *mapping, bool fullAxisOnly = false);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clears all keyboard, mouse and joystick settings.
|
||||||
|
*/
|
||||||
|
void ClearSettings();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prints current keyboard, mouse and joystick settings to stdout.
|
||||||
|
*/
|
||||||
|
void PrintSettings();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reads all keyboard, mouse and joystick settings (and any additional system-specific additional settings) from the given INI file.
|
||||||
|
*/
|
||||||
|
virtual void ReadFromINIFile(CINIFile *ini, const char *section);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Writes all keyboard, mouse and joystick settings (and any additional system-specific settings) to the given INI file.
|
||||||
|
*/
|
||||||
|
virtual void WriteToINIFile(CINIFile *ini, const char *section);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Waits for any input from the user and once received copies a mapping configuration representing the input (eg KEY_A or JOY1_AXIS_POS)
|
||||||
|
* into the given buffer.
|
||||||
|
* Returns true if input was successfully received or false if the user activated the given escape mapping or closed the window.
|
||||||
|
* readFlags specifies which types of inputs (keyboards, mice, joysticks) are to be read and whether to merge the inputs to a common
|
||||||
|
* mapping, eg return MOUSE_XAXIS rather than MOUSE3_XAXIS.
|
||||||
|
* If fullAxisOnly is true, then only mappings representing a full axis are returned, eg JOY1_XAXIS is allowed but not JOY1_XAXIS_POS.
|
||||||
|
*/
|
||||||
|
bool ReadMapping(char *buffer, unsigned bufSize, bool fullAxisOnly, unsigned readFlags = READ_ALL, const char *escapeMapping = "KEY_ESCAPE");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Updates the current state of the input system (called by CInputs.Poll).
|
||||||
|
*/
|
||||||
|
virtual bool Poll() = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Lets the input system know that inputs are being configured (called by CInputs.ConfigureInputs).
|
||||||
|
*/
|
||||||
|
virtual void ConfigStart();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Lets the input system know that inputs are no longer being configured (called by CInputs.ConfigureInputs).
|
||||||
|
*/
|
||||||
|
virtual void ConfigEnd();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sets the mouse visibility (some systems may choose to ignore this).
|
||||||
|
*/
|
||||||
|
virtual void SetMouseVisibility(bool visible) = 0;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Nested Classes
|
||||||
|
//
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Input source for a key on a keyboard.
|
||||||
|
*/
|
||||||
|
class CKeyInputSource : public CInputSource
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
CInputSystem *m_system; // Parent input system
|
||||||
|
int m_kbdNum; // Keyboard number
|
||||||
|
int m_keyIndex; // Key index
|
||||||
|
int m_incr; // Key increment for analog values
|
||||||
|
int m_decr; // Key decrement for analog values
|
||||||
|
int m_val; // Current analog key value
|
||||||
|
int m_maxVal; // Maximum analog key value
|
||||||
|
|
||||||
|
public:
|
||||||
|
CKeyInputSource(CInputSystem *system, int kbdNum, int keyIndex, unsigned sensitivity, unsigned decaySpeed);
|
||||||
|
|
||||||
|
bool GetValueAsSwitch(bool &val);
|
||||||
|
|
||||||
|
bool GetValueAsAnalog(int &val, int minVal, int offVal, int maxVal);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Input source for the X- or Y-axis of a mouse.
|
||||||
|
*/
|
||||||
|
class CMseAxisInputSource : public CInputSource
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
CInputSystem *m_system; // Parent input system
|
||||||
|
int m_mseNum; // Mouse number
|
||||||
|
int m_axisNum; // Axis number (0 = XAxis, 1 = YAxis, 2 = ZAxis)
|
||||||
|
int m_axisDir; // Axis direction (AXIS_FULL, AXIS_INVERTED, AXIS_POSITIVE or AXIS_NEGATIVE)
|
||||||
|
int m_deadPixels; // Size in pixels of dead zone in centre of axis
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Scales the mouse axis value to the given range.
|
||||||
|
*/
|
||||||
|
int ScaleAxisValue(int minVal, int offVal, int maxVal);
|
||||||
|
|
||||||
|
public:
|
||||||
|
CMseAxisInputSource(CInputSystem *system, int mseNum, int axisNum, int axisDir, unsigned deadZone);
|
||||||
|
|
||||||
|
bool GetValueAsSwitch(bool &val);
|
||||||
|
|
||||||
|
bool GetValueAsAnalog(int &val, int minVal, int offVal, int maxVal);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Input source for the button of a mouse.
|
||||||
|
*/
|
||||||
|
class CMseButInputSource : public CInputSource
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
CInputSystem *m_system; // Parent input system
|
||||||
|
int m_mseNum; // Mouse number
|
||||||
|
int m_butNum; // Button number
|
||||||
|
|
||||||
|
public:
|
||||||
|
CMseButInputSource(CInputSystem *system, int mseNum, int butNum);
|
||||||
|
|
||||||
|
bool GetValueAsSwitch(bool &val);
|
||||||
|
|
||||||
|
bool GetValueAsAnalog(int &val, int minVal, int offVal, int maxVal);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Input source for the axis of a joystick.
|
||||||
|
*/
|
||||||
|
class CJoyAxisInputSource : public CInputSource
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
CInputSystem *m_system; // Parent input system
|
||||||
|
int m_joyNum; // Joystick number
|
||||||
|
int m_axisNum; // Axis number (0 = XAxis, 1 = YAxis, 2 = ZAxis, 3 = RXAxis, 4 = RYAxis, 5 = RZAxis)
|
||||||
|
int m_axisDir; // Axis direction (AXIS_FULL, AXIS_INVERTED, AXIS_POSITIVE or AXIS_NEGATIVE)
|
||||||
|
int m_posDZone; // Dead zone for positive range
|
||||||
|
int m_negDZone; // Dead zone for negative range
|
||||||
|
int m_posSat; // Saturation for positive range
|
||||||
|
int m_negSat; // Saturation for negative range
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Scales the joystick axis value to the given range.
|
||||||
|
*/
|
||||||
|
int ScaleAxisValue(int minVal, int offVal, int maxVal);
|
||||||
|
|
||||||
|
public:
|
||||||
|
CJoyAxisInputSource(CInputSystem *system, int joyNum, int axisNum, int axisDir, unsigned deadZone, unsigned saturation);
|
||||||
|
|
||||||
|
bool GetValueAsSwitch(bool &val);
|
||||||
|
|
||||||
|
bool GetValueAsAnalog(int &val, int minVal, int offVal, int maxVal);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Input source for a particular direction of a POV hat controller of a joystick.
|
||||||
|
*/
|
||||||
|
class CJoyPOVInputSource : public CInputSource
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
CInputSystem *m_system; // Parent input system
|
||||||
|
int m_joyNum; // Joystick number
|
||||||
|
int m_povNum; // POV hat number
|
||||||
|
int m_povDir; // POV hat direction (POV_UP, POV_LEFT, POV_RIGHT, POV_DOWN)
|
||||||
|
|
||||||
|
public:
|
||||||
|
CJoyPOVInputSource(CInputSystem *system, int joyNum, int povNum, int povDir);
|
||||||
|
|
||||||
|
bool GetValueAsSwitch(bool &val);
|
||||||
|
|
||||||
|
bool GetValueAsAnalog(int &val, int minVal, int offVal, int maxVal);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Input source for the button of a joystick.
|
||||||
|
*/
|
||||||
|
class CJoyButInputSource : public CInputSource
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
CInputSystem *m_system; // Parent input system
|
||||||
|
int m_joyNum; // Joystick number
|
||||||
|
int m_butNum; // Button number
|
||||||
|
|
||||||
|
public:
|
||||||
|
CJoyButInputSource(CInputSystem *system, int joyNum, int butNum);
|
||||||
|
|
||||||
|
bool GetValueAsSwitch(bool &val);
|
||||||
|
|
||||||
|
bool GetValueAsAnalog(int &val, int minVal, int offVal, int maxVal);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // INCLUDED_INPUTSYSTEM_H
|
148
Inputs/InputTypes.cpp
Normal file
148
Inputs/InputTypes.cpp
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
#include "Supermodel.h"
|
||||||
|
|
||||||
|
// All the input subclasses have been grouped together here as they are very simple classes
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CSwitchInput
|
||||||
|
*/
|
||||||
|
CSwitchInput::CSwitchInput(const char *inputId, const char *inputLabel, unsigned inputGameFlags, const char *defaultMapping, UINT16 offVal, UINT16 onVal) :
|
||||||
|
CInput(inputId, inputLabel, INPUT_FLAGS_SWITCH, inputGameFlags, defaultMapping), m_offVal(offVal), m_onVal(onVal)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSwitchInput::Poll()
|
||||||
|
{
|
||||||
|
prevValue = value;
|
||||||
|
|
||||||
|
bool boolValue = !!value;
|
||||||
|
if (m_source != NULL && m_source->GetValueAsSwitch(boolValue))
|
||||||
|
value = (boolValue ? m_onVal : m_offVal);
|
||||||
|
else
|
||||||
|
value = m_offVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSwitchInput::Pressed()
|
||||||
|
{
|
||||||
|
return prevValue == m_offVal && value == m_onVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSwitchInput::Released()
|
||||||
|
{
|
||||||
|
return prevValue == m_onVal && value == m_offVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CAnalogInput
|
||||||
|
*/
|
||||||
|
CAnalogInput::CAnalogInput(const char *inputId, const char *inputLabel, unsigned inputGameFlags, const char *defaultMapping, UINT16 minVal, UINT16 maxVal) :
|
||||||
|
CInput(inputId, inputLabel, INPUT_FLAGS_ANALOG, inputGameFlags, defaultMapping, minVal), m_minVal(minVal), m_maxVal(maxVal)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
void CAnalogInput::Poll()
|
||||||
|
{
|
||||||
|
prevValue = value;
|
||||||
|
|
||||||
|
if (m_source == NULL)
|
||||||
|
{
|
||||||
|
value = m_minVal;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int intValue = value;
|
||||||
|
if (m_source->GetValueAsAnalog(intValue, m_minVal, m_minVal, m_maxVal))
|
||||||
|
value = intValue;
|
||||||
|
else
|
||||||
|
value = m_minVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CAnalogInput::HasValue()
|
||||||
|
{
|
||||||
|
return value > m_minVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
double CAnalogInput::ValueAsFraction()
|
||||||
|
{
|
||||||
|
double frac = (double)(value - m_minVal)/(double)(m_maxVal - m_minVal);
|
||||||
|
return (frac >= 0.0 ? frac : -frac);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CAxisInput
|
||||||
|
*/
|
||||||
|
CAxisInput::CAxisInput(const char *inputId, const char *inputLabel, unsigned inputGameFlags, const char *defaultMapping,
|
||||||
|
CAnalogInput *negInput, CAnalogInput *posInput, UINT16 minVal, UINT16 offVal, UINT16 maxVal) :
|
||||||
|
CInput(inputId, inputLabel, INPUT_FLAGS_AXIS, inputGameFlags, defaultMapping, offVal), m_negInput(negInput), m_posInput(posInput),
|
||||||
|
m_minVal(minVal), m_offVal(offVal), m_maxVal(maxVal)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
void CAxisInput::Poll()
|
||||||
|
{
|
||||||
|
prevValue = value;
|
||||||
|
|
||||||
|
// Try getting value from analog inputs that represent negative and positive range of the axis first and then try the default input source
|
||||||
|
int intValue = value;
|
||||||
|
if ((m_negInput != NULL && m_negInput->HasValue()) || (m_posInput != NULL && m_posInput->HasValue()))
|
||||||
|
{
|
||||||
|
if (m_maxVal > m_minVal)
|
||||||
|
{
|
||||||
|
value = m_offVal;
|
||||||
|
if (m_posInput != NULL) value += (int)(m_posInput->ValueAsFraction() * (double)(m_maxVal - m_offVal));
|
||||||
|
if (m_negInput != NULL) value -= (int)(m_negInput->ValueAsFraction() * (double)(m_offVal - m_minVal));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value = m_offVal;
|
||||||
|
if (m_posInput != NULL) value += (int)(m_posInput->ValueAsFraction() * (double)(m_offVal - m_maxVal));
|
||||||
|
if (m_negInput != NULL) value -= (int)(m_negInput->ValueAsFraction() * (double)(m_minVal - m_offVal));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (m_source != NULL && m_source->GetValueAsAnalog(intValue, m_minVal, m_offVal, m_maxVal))
|
||||||
|
value = intValue;
|
||||||
|
else
|
||||||
|
value = m_offVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CAxisInput::HasValue()
|
||||||
|
{
|
||||||
|
return value != m_offVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
double CAxisInput::ValueAsFraction()
|
||||||
|
{
|
||||||
|
double frac = (double)(value - m_minVal)/(double)(m_maxVal - m_minVal);
|
||||||
|
return (frac >= 0.0 ? frac : -frac);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CGearShift4Input
|
||||||
|
*/
|
||||||
|
CGearShift4Input::CGearShift4Input(const char *inputId, const char *inputLabel, unsigned inputGameFlags,
|
||||||
|
CSwitchInput *shift1Input, CSwitchInput *shift2Input, CSwitchInput *shift3Input, CSwitchInput *shift4Input,
|
||||||
|
CSwitchInput *shiftUpInput, CSwitchInput *shiftDownInput) :
|
||||||
|
CInput(inputId, inputLabel, INPUT_FLAGS_VIRTUAL, inputGameFlags),
|
||||||
|
m_shift1Input(shift1Input), m_shift2Input(shift2Input), m_shift3Input(shift3Input), m_shift4Input(shift4Input),
|
||||||
|
m_shiftUpInput(shiftUpInput), m_shiftDownInput(shiftDownInput)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGearShift4Input::Poll()
|
||||||
|
{
|
||||||
|
prevValue = value;
|
||||||
|
|
||||||
|
// Neutral is when all gear buttons are released so shifting here is implemented as follows:
|
||||||
|
// Gears (values 1-4) are set by pressing a button (lower gears have priority) and "stick" until a shift to another gear or until the
|
||||||
|
// button is pressed again, at which point neutral (value 0) is assumed.
|
||||||
|
if (m_shift1Input->Pressed()) value = (value == 1 ? 0 : 1);
|
||||||
|
else if (m_shift2Input->Pressed()) value = (value == 2 ? 0 : 2);
|
||||||
|
else if (m_shift3Input->Pressed()) value = (value == 3 ? 0 : 3);
|
||||||
|
else if (m_shift4Input->Pressed()) value = (value == 4 ? 0 : 4);
|
||||||
|
|
||||||
|
// Also the shift up/down controls can increase/decrease the gears too
|
||||||
|
if (m_shiftUpInput->Pressed()) value = CInputSource::Clamp(value + 1, 0, 4);
|
||||||
|
else if (m_shiftDownInput->Pressed()) value = CInputSource::Clamp(value - 1, 0, 4);
|
||||||
|
}
|
135
Inputs/InputTypes.h
Normal file
135
Inputs/InputTypes.h
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
#ifndef INCLUDED_INPUTTYPES_H
|
||||||
|
#define INCLUDED_INPUTTYPES_H
|
||||||
|
|
||||||
|
#include "Types.h"
|
||||||
|
#include "Input.h"
|
||||||
|
|
||||||
|
// All the input subclasses have been grouped together here as they are very simple classes
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Represents a switch input (such as a button or 8-way joystick switch) whose value can be either on (usually 1) or off (usually 0).
|
||||||
|
*/
|
||||||
|
class CSwitchInput : public CInput
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// On and off values
|
||||||
|
UINT16 m_offVal;
|
||||||
|
UINT16 m_onVal;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CSwitchInput(const char *inputId, const char *inputLabel, unsigned inputGameFlags, const char *defaultMapping,
|
||||||
|
UINT16 offVal = 0x00, UINT16 onVal = 0x01);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Polls (updates) the input, updating its value from the input source
|
||||||
|
*/
|
||||||
|
void Poll();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if the input was pressed during last update (ie currently on but previously off)
|
||||||
|
*/
|
||||||
|
bool Pressed();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if the input was released duing last update (ie currently off but previously on)
|
||||||
|
*/
|
||||||
|
bool Released();
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Represents an analog input (such as a pedal) whose value ranges from a minimum 'off' value (usually 0) to a maximum 'on' value (usually 0xFF).
|
||||||
|
*/
|
||||||
|
class CAnalogInput : public CInput
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// Min and max values
|
||||||
|
UINT16 m_minVal;
|
||||||
|
UINT16 m_maxVal;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CAnalogInput(const char *inputId, const char *inputLabel, unsigned inputGameFlags, const char *defaultMapping,
|
||||||
|
UINT16 minVal = 0x00, UINT16 maxVal = 0xFF);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Polls (updates) the input, updating its value from the input source
|
||||||
|
*/
|
||||||
|
void Poll();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if the input is currently activated
|
||||||
|
*/
|
||||||
|
bool HasValue();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns current value as a fraction 0.0 to 1.0 of the full range
|
||||||
|
*/
|
||||||
|
double ValueAsFraction();
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Represents an axis input (such as a joystick axis, lightgun axis or steering wheel) whose value ranges from a minimum value (usually 0) to a maximum
|
||||||
|
* value (usually 0xFF) with an off value (usually 0x80) somewhere in-between.
|
||||||
|
* As well as having its own input source it takes two analog inputs that can represent the negative and positive ranges of the axis.
|
||||||
|
*/
|
||||||
|
class CAxisInput : public CInput
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// Analog inputs that represent the negative and positive range of the axis
|
||||||
|
CAnalogInput *m_negInput;
|
||||||
|
CAnalogInput *m_posInput;
|
||||||
|
|
||||||
|
// Min, off and max values
|
||||||
|
UINT16 m_minVal;
|
||||||
|
UINT16 m_offVal;
|
||||||
|
UINT16 m_maxVal;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CAxisInput(const char *inputId, const char *inputLabel, unsigned inputGameFlags, const char *defaultMapping, CAnalogInput *negInput, CAnalogInput *posInput,
|
||||||
|
UINT16 minVal = 0x00, UINT16 offVal = 0x80, UINT16 maxVal = 0xFF);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Polls (updates) the input, updating its value from the switch inputs and/or input source
|
||||||
|
*/
|
||||||
|
void Poll();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if the input is currently activated (ie axis is not centered)
|
||||||
|
*/
|
||||||
|
bool HasValue();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns current value as a fraction -1.0 to 1.0
|
||||||
|
*/
|
||||||
|
double ValueAsFraction();
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Represents a 4-gear shift input whose value ranges from 0 to 4, where neutral is 0 and 1-4 represents a gear selection.
|
||||||
|
* It takes six switch inputs that combine together to control the gear shifting. The first four inputs can set the gear
|
||||||
|
* directly, while the last two allow shifting up or down through the gears.
|
||||||
|
*/
|
||||||
|
class CGearShift4Input : public CInput
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// Four switch inputs for gears 1-4
|
||||||
|
CSwitchInput *m_shift1Input;
|
||||||
|
CSwitchInput *m_shift2Input;
|
||||||
|
CSwitchInput *m_shift3Input;
|
||||||
|
CSwitchInput *m_shift4Input;
|
||||||
|
|
||||||
|
// Two switch inputs for up/down gear
|
||||||
|
CSwitchInput *m_shiftUpInput;
|
||||||
|
CSwitchInput *m_shiftDownInput;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CGearShift4Input(const char *inputId, const char *inputLabel, unsigned inputGameFlags,
|
||||||
|
CSwitchInput *shift1Input, CSwitchInput *shift2Input, CSwitchInput *shift3Input, CSwitchInput *shift4Input,
|
||||||
|
CSwitchInput *shiftUpInput, CSwitchInput *shiftDownInput);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Polls (updates) the input, updating its value from the switch inputs
|
||||||
|
*/
|
||||||
|
void Poll();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // INCLUDED_INPUTTYPES_H
|
535
Inputs/Inputs.cpp
Normal file
535
Inputs/Inputs.cpp
Normal file
|
@ -0,0 +1,535 @@
|
||||||
|
#include "Supermodel.h"
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <iostream>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#define UI_INPUT 0
|
||||||
|
|
||||||
|
CInputs::CInputs(CInputSystem *system) : m_system(system)
|
||||||
|
{
|
||||||
|
// UI Controls
|
||||||
|
uiExit = AddSwitchInput("UIExit", "Exit UI", UI_INPUT, "KEY_ESCAPE");
|
||||||
|
uiReset = AddSwitchInput("UIReset", "Reset", UI_INPUT, "KEY_ALT+KEY_R");
|
||||||
|
uiPause = AddSwitchInput("UIPause", "Pause", UI_INPUT, "KEY_PAUSE");
|
||||||
|
uiSaveState = AddSwitchInput("UISaveState", "Save State", UI_INPUT, "KEY_F5");
|
||||||
|
uiChangeSlot = AddSwitchInput("UIChangeSlot", "Change Save Slot", UI_INPUT, "KEY_F6");
|
||||||
|
uiLoadState = AddSwitchInput("UILoadState", "Load State", UI_INPUT, "KEY_F7");
|
||||||
|
uiDumpInpState = AddSwitchInput("UIDumpInputState", "Dump Input State", UI_INPUT, "KEY_F8");
|
||||||
|
uiClearNVRAM = AddSwitchInput("UIClearNVRAM", "Clear NVRAM", UI_INPUT, "KEY_ALT+KEY_N");
|
||||||
|
uiToggleCursor = AddSwitchInput("UIToggleCursor", "Toggle Cursor", UI_INPUT, "KEY_ALT+KEY_I");
|
||||||
|
uiToggleFrLimit = AddSwitchInput("UIToggleFrameLimit", "Toggle Frame Limiting", UI_INPUT, "KEY_ALT+KEY_T");
|
||||||
|
|
||||||
|
// Common Controls
|
||||||
|
start[0] = AddSwitchInput("Start1", "P1 Start", GAME_INPUT_COMMON, "KEY_1,JOY1_BUTTON9");
|
||||||
|
start[1] = AddSwitchInput("Start2", "P2 Start", GAME_INPUT_COMMON, "KEY_2,JOY2_BUTTON9");
|
||||||
|
coin[0] = AddSwitchInput("Coin1", "P1 Coin", GAME_INPUT_COMMON, "KEY_3,JOY1_BUTTON10");
|
||||||
|
coin[1] = AddSwitchInput("Coin2", "P2 Coin", GAME_INPUT_COMMON, "KEY_4,JOY2_BUTTON10");
|
||||||
|
service[0] = AddSwitchInput("ServiceA", "Service A", GAME_INPUT_COMMON, "KEY_5");
|
||||||
|
service[1] = AddSwitchInput("ServiceB", "Service B", GAME_INPUT_COMMON, "KEY_7");
|
||||||
|
test[0] = AddSwitchInput("TestA", "Test A", GAME_INPUT_COMMON, "KEY_6");
|
||||||
|
test[1] = AddSwitchInput("TestB", "Test B", GAME_INPUT_COMMON, "KEY_8");
|
||||||
|
|
||||||
|
// 8-Way Joysticks
|
||||||
|
up[0] = AddSwitchInput("JoyUp", "P1 Joystick Up", GAME_INPUT_JOYSTICK1, "KEY_UP,JOY1_UP");
|
||||||
|
down[0] = AddSwitchInput("JoyDown", "P1 Joystick Down", GAME_INPUT_JOYSTICK1, "KEY_DOWN,JOY1_DOWN");
|
||||||
|
left[0] = AddSwitchInput("JoyLeft", "P1 Joystick Left", GAME_INPUT_JOYSTICK1, "KEY_LEFT,JOY1_LEFT");
|
||||||
|
right[0] = AddSwitchInput("JoyRight", "P1 Joystick Right", GAME_INPUT_JOYSTICK1, "KEY_RIGHT,JOY1_RIGHT");
|
||||||
|
up[1] = AddSwitchInput("JoyUp2", "P2 Joystick Up", GAME_INPUT_JOYSTICK2, "JOY2_UP");
|
||||||
|
down[1] = AddSwitchInput("JoyDown2", "P2 Joystick Down", GAME_INPUT_JOYSTICK2, "JOY2_DOWN");
|
||||||
|
left[1] = AddSwitchInput("JoyLeft2", "P2 Joystick Left", GAME_INPUT_JOYSTICK2, "JOY2_LEFT");
|
||||||
|
right[1] = AddSwitchInput("JoyRight2", "P2 Joystick Right", GAME_INPUT_JOYSTICK2, "JOY2_RIGHT");
|
||||||
|
|
||||||
|
// Fighting Game Buttons
|
||||||
|
punch[0] = AddSwitchInput("Punch", "P1 Punch", GAME_INPUT_FIGHTING, "KEY_A,JOY1_BUTTON1");
|
||||||
|
kick[0] = AddSwitchInput("Kick", "P1 Kick", GAME_INPUT_FIGHTING, "KEY_S,JOY1_BUTTON2");
|
||||||
|
guard[0] = AddSwitchInput("Guard", "P1 Guard", GAME_INPUT_FIGHTING, "KEY_D,JOY1_BUTTON3");
|
||||||
|
escape[0] = AddSwitchInput("Escape", "P1 Escape", GAME_INPUT_FIGHTING, "KEY_F,JOY1_BUTTON4");
|
||||||
|
punch[1] = AddSwitchInput("Punch2", "P2 Punch", GAME_INPUT_FIGHTING, "JOY2_BUTTON1");
|
||||||
|
kick[1] = AddSwitchInput("Kick2", "P2 Kick", GAME_INPUT_FIGHTING, "JOY2_BUTTON2");
|
||||||
|
guard[1] = AddSwitchInput("Guard2", "P2 Guard", GAME_INPUT_FIGHTING, "JOY2_BUTTON3");
|
||||||
|
escape[1] = AddSwitchInput("Escape2", "P2 Escape", GAME_INPUT_FIGHTING, "JOY2_BUTTON4");
|
||||||
|
|
||||||
|
// Virtua Striker Buttons
|
||||||
|
shortPass[0] = AddSwitchInput("ShortPass", "P1 Short Pass", GAME_INPUT_SOCCER, "KEY_A,JOY1_BUTTON1");
|
||||||
|
longPass[0] = AddSwitchInput("LongPass", "P1 Long Pass", GAME_INPUT_SOCCER, "KEY_S,JOY1_BUTTON2");
|
||||||
|
shoot[0] = AddSwitchInput("Shoot", "P1 Shoot", GAME_INPUT_SOCCER, "KEY_D,JOY1_BUTTON3");
|
||||||
|
shortPass[1] = AddSwitchInput("ShortPass1", "P2 Short Pass", GAME_INPUT_SOCCER, "JOY2_BUTTON1");
|
||||||
|
longPass[1] = AddSwitchInput("LongPass1", "P2 Long Pass", GAME_INPUT_SOCCER, "JOY2_BUTTON2");
|
||||||
|
shoot[1] = AddSwitchInput("Shoot1", "P2 Shoot", GAME_INPUT_SOCCER, "JOY2_BUTTON3");
|
||||||
|
|
||||||
|
// Racing Game Steering Controls
|
||||||
|
CAnalogInput* steeringLeft = AddAnalogInput("SteeringLeft", "Steer Left", GAME_INPUT_VEHICLE, "KEY_LEFT");
|
||||||
|
CAnalogInput* steeringRight = AddAnalogInput("SteeringRight", "Steer Right", GAME_INPUT_VEHICLE, "KEY_RIGHT");
|
||||||
|
|
||||||
|
steering = AddAxisInput ("Steering", "Full Steering", GAME_INPUT_VEHICLE, "JOY1_XAXIS", steeringLeft, steeringRight);
|
||||||
|
accelerator = AddAnalogInput("Accelerator", "Accelerator Pedal", GAME_INPUT_VEHICLE, "KEY_UP,JOY1_UP");
|
||||||
|
brake = AddAnalogInput("Brake", "Brake Pedal", GAME_INPUT_VEHICLE, "KEY_DOWN,JOY1_DOWN");
|
||||||
|
|
||||||
|
// Racing Game Gear Shift
|
||||||
|
CSwitchInput *shift1 = AddSwitchInput("GearShift1", "Shift 1/Up", GAME_INPUT_SHIFT4, "KEY_Q,JOY1_BUTTON5");
|
||||||
|
CSwitchInput *shift2 = AddSwitchInput("GearShift2", "Shift 2/Down", GAME_INPUT_SHIFT4, "KEY_W,JOY1_BUTTON6");
|
||||||
|
CSwitchInput *shift3 = AddSwitchInput("GearShift3", "Shift 3", GAME_INPUT_SHIFT4, "KEY_E,JOY1_BUTTON7");
|
||||||
|
CSwitchInput *shift4 = AddSwitchInput("GearShift4", "Shift 4", GAME_INPUT_SHIFT4, "KEY_R,JOY1_BUTTON8");
|
||||||
|
CSwitchInput *shiftUp = AddSwitchInput("GearShiftUp", "Shift Up", GAME_INPUT_SHIFT4, "NONE");
|
||||||
|
CSwitchInput *shiftDown = AddSwitchInput("GearShiftDown", "Shift Down", GAME_INPUT_SHIFT4, "NONE");
|
||||||
|
|
||||||
|
gearShift4 = AddGearShift4Input("GearShift", "Gear Shift", GAME_INPUT_SHIFT4, shift1, shift2, shift3, shift4, shiftUp, shiftDown);
|
||||||
|
|
||||||
|
// Racing Game VR View Buttons
|
||||||
|
vr[0] = AddSwitchInput("VR1", "VR1", GAME_INPUT_VR, "KEY_A,JOY1_BUTTON1");
|
||||||
|
vr[1] = AddSwitchInput("VR2", "VR2", GAME_INPUT_VR, "KEY_S,JOY1_BUTTON2");
|
||||||
|
vr[2] = AddSwitchInput("VR3", "VR3", GAME_INPUT_VR, "KEY_D,JOY1_BUTTON3");
|
||||||
|
vr[3] = AddSwitchInput("VR4", "VR4", GAME_INPUT_VR, "KEY_F,JOY1_BUTTON4");
|
||||||
|
|
||||||
|
// Sega Rally Buttons
|
||||||
|
viewChange = AddSwitchInput("ViewChange", "View Change", GAME_INPUT_RALLY, "KEY_A,JOY1_BUTTON1");
|
||||||
|
handBrake = AddSwitchInput("HandBrake", "Hand Brake", GAME_INPUT_RALLY, "KEY_S,JOY1_BUTTON2");
|
||||||
|
|
||||||
|
// Virtua On Controls
|
||||||
|
twinJoyTurnLeft = AddSwitchInput("TwinJoyTurnLeft", "Turn Left", GAME_INPUT_TWIN_JOYSTICKS, "KEY_Q,JOY1_RZAXIS_POS");
|
||||||
|
twinJoyTurnRight = AddSwitchInput("TwinJoyTurnRight", "Turn Right", GAME_INPUT_TWIN_JOYSTICKS, "KEY_W,JOY1_RZAXIS_NEG");
|
||||||
|
twinJoyForward = AddSwitchInput("TwinJoyForward", "Forward", GAME_INPUT_TWIN_JOYSTICKS, "KEY_UP,JOY1_UP");
|
||||||
|
twinJoyReverse = AddSwitchInput("TwinJoyReverse", "Reverse", GAME_INPUT_TWIN_JOYSTICKS, "KEY_DOWN,JOY1_DOWN");
|
||||||
|
twinJoyStrafeLeft = AddSwitchInput("TwinJoyStrafeLeft", "Strafe Left", GAME_INPUT_TWIN_JOYSTICKS, "KEY_LEFT,JOY1_LEFT");
|
||||||
|
twinJoyStrafeRight = AddSwitchInput("TwinJoyStrafeRight", "Strafe Right", GAME_INPUT_TWIN_JOYSTICKS, "KEY_RIGHT,JOY1_RIGHT");
|
||||||
|
twinJoyJump = AddSwitchInput("TwinJoyJump", "Jump", GAME_INPUT_TWIN_JOYSTICKS, "KEY_E,JOY1_BUTTON1");
|
||||||
|
twinJoyCrouch = AddSwitchInput("TwinJoyCrouch", "Crouch", GAME_INPUT_TWIN_JOYSTICKS, "KEY_R,JOY1_BUTTON2");
|
||||||
|
twinJoyLeftShot = AddSwitchInput("TwinJoyLeftShot", "Left Shot Trigger", GAME_INPUT_TWIN_JOYSTICKS, "KEY_A,JOY1_BUTTON5");
|
||||||
|
twinJoyRightShot = AddSwitchInput("TwinJoyRightShot", "Right Shot Trigger", GAME_INPUT_TWIN_JOYSTICKS, "KEY_S,JOY1_BUTTON6");
|
||||||
|
twinJoyLeftTurbo = AddSwitchInput("TwinJoyLeftTurbo", "Left Turbo", GAME_INPUT_TWIN_JOYSTICKS, "KEY_Z,JOY1_BUTTON7");
|
||||||
|
twinJoyRightTurbo = AddSwitchInput("TwinJoyRightTurbo", "Right Turbo", GAME_INPUT_TWIN_JOYSTICKS, "KEY_X,JOY1_BUTTON8");
|
||||||
|
|
||||||
|
// Analog Joystick
|
||||||
|
CAnalogInput *analogJoyLeft = AddAnalogInput("AnalogJoyLeft", "Analog Left", GAME_INPUT_ANALOG_JOYSTICK, "KEY_LEFT");
|
||||||
|
CAnalogInput *analogJoyRight = AddAnalogInput("AnalogJoyRight", "Analog Right", GAME_INPUT_ANALOG_JOYSTICK, "KEY_RIGHT");
|
||||||
|
CAnalogInput *analogJoyUp = AddAnalogInput("AnalogJoyUp", "Analog Up", GAME_INPUT_ANALOG_JOYSTICK, "KEY_UP");
|
||||||
|
CAnalogInput *analogJoyDown = AddAnalogInput("AnalogJoyDown", "Analog Down", GAME_INPUT_ANALOG_JOYSTICK, "KEY_DOWN");
|
||||||
|
|
||||||
|
analogJoyX = AddAxisInput ("AnalogJoyX", "Analog X-Axis", GAME_INPUT_ANALOG_JOYSTICK, "JOY_XAXIS,MOUSE_XAXIS", analogJoyLeft, analogJoyRight);
|
||||||
|
analogJoyY = AddAxisInput ("AnalogJoyY", "Analog Y-Axis", GAME_INPUT_ANALOG_JOYSTICK, "JOY_YAXIS,MOUSE_YAXIS", analogJoyUp, analogJoyDown);
|
||||||
|
analogJoyTrigger = AddSwitchInput("AnalogJoyTrigger", "Trigger Button", GAME_INPUT_ANALOG_JOYSTICK, "KEY_A,JOY_BUTTON1,MOUSE_LEFT_BUTTON");
|
||||||
|
analogJoyEvent = AddSwitchInput("AnalogJoyEvent", "Event Button", GAME_INPUT_ANALOG_JOYSTICK, "KEY_S,JOY_BUTTON2,MOUSE_RIGHT_BUTTON");
|
||||||
|
|
||||||
|
// Lightguns
|
||||||
|
CAnalogInput *gun1Left = AddAnalogInput("GunLeft", "P1 Gun Left", GAME_INPUT_GUN1, "KEY_LEFT");
|
||||||
|
CAnalogInput *gun1Right = AddAnalogInput("GunRight", "P1 Gun Right", GAME_INPUT_GUN1, "KEY_RIGHT");
|
||||||
|
CAnalogInput *gun1Up = AddAnalogInput("GunUp", "P1 Gun Up", GAME_INPUT_GUN1, "KEY_UP");
|
||||||
|
CAnalogInput *gun1Down = AddAnalogInput("GunDown", "P1 Gun Down", GAME_INPUT_GUN1, "KEY_DOWN");
|
||||||
|
|
||||||
|
gunX[0] = AddAxisInput ("GunX", "P1 Gun X-Axis", GAME_INPUT_GUN1, "JOY1_XAXIS,MOUSE_XAXIS", gun1Left, gun1Right, 150, 400, 651); // normalize to [150,651]
|
||||||
|
gunY[0] = AddAxisInput ("GunY", "P1 Gun Y-Axis", GAME_INPUT_GUN1, "JOY1_YAXIS,MOUSE_YAXIS", gun1Up, gun1Down, 80, 272, 465); // normalize to [80,465]
|
||||||
|
trigger[0] = AddSwitchInput("Trigger", "P1 Trigger", GAME_INPUT_GUN1, "KEY_A,JOY1_BUTTON1,MOUSE_LEFT_BUTTON");
|
||||||
|
offscreen[0] = AddSwitchInput("Offscreen", "P1 Point Off-screen", GAME_INPUT_GUN1, "KEY_S,JOY1_BUTTON2,MOUSE_RIGHT_BUTTON");
|
||||||
|
|
||||||
|
CAnalogInput *gun2Left = AddAnalogInput("GunLeft2", "P2 Gun Left", GAME_INPUT_GUN2, "NONE");
|
||||||
|
CAnalogInput *gun2Right = AddAnalogInput("GunRight2", "P2 Gun Right", GAME_INPUT_GUN2, "NONE");
|
||||||
|
CAnalogInput *gun2Up = AddAnalogInput("GunUp2", "P2 Gun Up", GAME_INPUT_GUN2, "NONE");
|
||||||
|
CAnalogInput *gun2Down = AddAnalogInput("GunDown2", "P2 Gun Down", GAME_INPUT_GUN2, "NONE");
|
||||||
|
|
||||||
|
gunX[1] = AddAxisInput ("GunX2", "P2 Gun X-Axis", GAME_INPUT_GUN2, "JOY2_XAXIS", gun2Left, gun2Right, 150, 400, 651); // normalize to [150,651]
|
||||||
|
gunY[1] = AddAxisInput ("GunY2", "P2 Gun Y-Axis", GAME_INPUT_GUN2, "JOY2_YAXIS", gun2Up, gun2Down, 80, 272, 465); // normalize to [80,465]
|
||||||
|
trigger[1] = AddSwitchInput("Trigger2", "P2 Trigger", GAME_INPUT_GUN2, "JOY2_BUTTON1");
|
||||||
|
offscreen[1] = AddSwitchInput("Offscreen2", "P2 Point Off-screen", GAME_INPUT_GUN2, "JOY2_BUTTON2");
|
||||||
|
}
|
||||||
|
|
||||||
|
CInputs::~CInputs()
|
||||||
|
{
|
||||||
|
for (vector<CInput*>::iterator it = m_inputs.begin(); it < m_inputs.end(); it++)
|
||||||
|
delete *it;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSwitchInput* CInputs::AddSwitchInput(const char *id, const char *label, unsigned gameFlags, const char *defaultMapping,
|
||||||
|
UINT16 offVal, UINT16 onVal)
|
||||||
|
{
|
||||||
|
CSwitchInput *input = new CSwitchInput(id, label, gameFlags, defaultMapping, offVal, onVal);
|
||||||
|
m_inputs.push_back(input);
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
CAnalogInput* CInputs::AddAnalogInput(const char *id, const char *label, unsigned gameFlags, const char *defaultMapping,
|
||||||
|
UINT16 minVal, UINT16 maxVal)
|
||||||
|
{
|
||||||
|
CAnalogInput *input = new CAnalogInput(id, label, gameFlags, defaultMapping, minVal, maxVal);
|
||||||
|
m_inputs.push_back(input);
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
CAxisInput* CInputs::AddAxisInput(const char *id, const char *label, unsigned gameFlags, const char *defaultMapping,
|
||||||
|
CAnalogInput* axisNeg, CAnalogInput* axisPos, UINT16 minVal, UINT16 offVal, UINT16 maxVal)
|
||||||
|
{
|
||||||
|
CAxisInput *input = new CAxisInput(id, label, gameFlags, defaultMapping, axisNeg, axisPos, minVal, offVal, maxVal);
|
||||||
|
m_inputs.push_back(input);
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
CGearShift4Input* CInputs::AddGearShift4Input(const char *id, const char *label, unsigned gameFlags,
|
||||||
|
CSwitchInput *shift1, CSwitchInput *shift2, CSwitchInput *shift3, CSwitchInput *shift4, CSwitchInput *shiftUp, CSwitchInput *shiftDown)
|
||||||
|
{
|
||||||
|
CGearShift4Input *input = new CGearShift4Input(id, label, gameFlags, shift1, shift2, shift3, shift4, shiftUp, shiftDown);
|
||||||
|
m_inputs.push_back(input);
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CInputs::PrintHeader(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
char header[1024];
|
||||||
|
va_list vl;
|
||||||
|
va_start(vl, fmt);
|
||||||
|
vsprintf(header, fmt, vl);
|
||||||
|
va_end(vl);
|
||||||
|
|
||||||
|
puts(header);
|
||||||
|
for (size_t i = 0; i < strlen(header); i++)
|
||||||
|
putchar('-');
|
||||||
|
printf("\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CInputs::PrintConfigureInputsHelp()
|
||||||
|
{
|
||||||
|
puts("For each control, use the following keys to map the inputs:");
|
||||||
|
puts("");
|
||||||
|
puts(" Return Set current input mapping and move to next control,");
|
||||||
|
puts(" c Clear current input mapping and remain there,");
|
||||||
|
puts(" s Set the current input mapping and remain there,");
|
||||||
|
puts(" a Append to current input mapping (for multiple assignments)");
|
||||||
|
puts(" and remain there,");
|
||||||
|
puts(" r Reset current input mapping to default and remain there,");
|
||||||
|
puts(" Down Move onto next control,");
|
||||||
|
puts(" Up Go back to previous control,");
|
||||||
|
puts(" h Display this help again,");
|
||||||
|
puts(" q Finish and save all changes,");
|
||||||
|
puts(" Esc Finish without saving any changes.");
|
||||||
|
puts("");
|
||||||
|
puts("To assign inputs, simply press the appropriate key, mouse button or");
|
||||||
|
puts("joystick button or move the mouse along an axis or move a joystick's");
|
||||||
|
puts("axis or POV hat controller.");
|
||||||
|
puts("");
|
||||||
|
puts("NOTES:");
|
||||||
|
puts(" - in order to assign a key the configuration window must on top,");
|
||||||
|
puts(" - in order to assign a mouse button the mouse must be clicked");
|
||||||
|
puts(" within the window,");
|
||||||
|
puts(" - in order to assign a mouse axis, the cursor must be placed in");
|
||||||
|
puts(" the center of the window and moved in the corresponding");
|
||||||
|
puts(" direction to the window's edge and then returned to the center.");
|
||||||
|
puts("");
|
||||||
|
}
|
||||||
|
|
||||||
|
CInputSystem *CInputs::GetInputSystem()
|
||||||
|
{
|
||||||
|
return m_system;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CInputs::Initialize()
|
||||||
|
{
|
||||||
|
// Make sure the input system is initialized too
|
||||||
|
if (!m_system->Initialize())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Initialize all the inputs
|
||||||
|
for (vector<CInput*>::iterator it = m_inputs.begin(); it < m_inputs.end(); it++)
|
||||||
|
(*it)->Initialize(m_system);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CInputs::InputIsConfigurable(CInput *input)
|
||||||
|
{
|
||||||
|
// All inputs except UI and virtual ones can be configured by the user
|
||||||
|
return input->flags != UI_INPUT && !(input->flags & INPUT_FLAGS_VIRTUAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
CInput* CInputs::LookupInputByID(const char* id)
|
||||||
|
{
|
||||||
|
for (vector<CInput*>::iterator it = m_inputs.begin(); it < m_inputs.end(); it++)
|
||||||
|
{
|
||||||
|
if (stricmp(id, (*it)->id) == 0)
|
||||||
|
return *it;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CInputs::ReadFromINIFile(CINIFile *ini, const char *section)
|
||||||
|
{
|
||||||
|
for (vector<CInput*>::iterator it = m_inputs.begin(); it < m_inputs.end(); it++)
|
||||||
|
{
|
||||||
|
if (!InputIsConfigurable(*it))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
string key("Input");
|
||||||
|
key.append((*it)->id);
|
||||||
|
string mapping;
|
||||||
|
if (ini->Get(section, key, mapping) == OKAY)
|
||||||
|
(*it)->SetMapping(mapping.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CInputs::WriteToINIFile(CINIFile *ini, const char *section)
|
||||||
|
{
|
||||||
|
for (vector<CInput*>::iterator it = m_inputs.begin(); it < m_inputs.end(); it++)
|
||||||
|
{
|
||||||
|
if (!InputIsConfigurable(*it))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
string key("Input");
|
||||||
|
key.append((*it)->id);
|
||||||
|
ini->Set(section, key, (*it)->GetMapping());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CInputs::ConfigureInputs(const GameInfo *game, unsigned dispX, unsigned dispY, unsigned dispW, unsigned dispH)
|
||||||
|
{
|
||||||
|
m_system->ConfigStart();
|
||||||
|
|
||||||
|
// Let the input system know the display geometry
|
||||||
|
m_system->SetDisplayGeom(dispX, dispY, dispW, dispH);
|
||||||
|
|
||||||
|
// Print header and help message
|
||||||
|
int gameFlags;
|
||||||
|
if (game != NULL)
|
||||||
|
{
|
||||||
|
PrintHeader("Configure Inputs for %s", game->title);
|
||||||
|
gameFlags = game->inputFlags;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PrintHeader("Configure Inputs");
|
||||||
|
gameFlags = GAME_INPUT_ALL;
|
||||||
|
}
|
||||||
|
PrintConfigureInputsHelp();
|
||||||
|
|
||||||
|
// Get all inputs to be configured
|
||||||
|
vector<CInput*> toConfigure;
|
||||||
|
vector<CInput*>::iterator it;
|
||||||
|
for (it = m_inputs.begin(); it < m_inputs.end(); it++)
|
||||||
|
{
|
||||||
|
if (InputIsConfigurable(*it) && ((*it)->gameFlags & gameFlags))
|
||||||
|
toConfigure.push_back(*it);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remember current mappings for each input in case changes need to be undone later
|
||||||
|
vector<string> oldMappings(toConfigure.size());
|
||||||
|
size_t index = 0;
|
||||||
|
for (it = toConfigure.begin(); it < toConfigure.end(); it++)
|
||||||
|
oldMappings[index++] = (*it)->GetMapping();
|
||||||
|
|
||||||
|
const char *groupLabel = NULL;
|
||||||
|
|
||||||
|
// Loop through all the inputs to be configured
|
||||||
|
index = 0;
|
||||||
|
while (index < toConfigure.size())
|
||||||
|
{
|
||||||
|
// Get the current input
|
||||||
|
CInput* input = toConfigure[index];
|
||||||
|
|
||||||
|
// If have moved to a new input group, print the group heading
|
||||||
|
const char *itGroupLabel = input->GetInputGroup();
|
||||||
|
if (groupLabel == NULL || stricmp(groupLabel, itGroupLabel) != 0)
|
||||||
|
{
|
||||||
|
groupLabel = itGroupLabel;
|
||||||
|
printf("%s:\n", groupLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
Redisplay:
|
||||||
|
// Print the input label, current input mapping and available options
|
||||||
|
if (index > 0)
|
||||||
|
printf(" %s [%s]: Ret/c/s/a/r/Up/Down/h/q/Esc? ", input->label, input->GetMapping());
|
||||||
|
else
|
||||||
|
printf(" %s [%s]: Ret/c/s/a/r/Down/h/q/Esc? ", input->label, input->GetMapping());
|
||||||
|
fflush(stdout); // required on terminals that use buffering
|
||||||
|
|
||||||
|
// Loop until user has selected a valid option
|
||||||
|
bool done = false;
|
||||||
|
char mapping[50];
|
||||||
|
while (!done)
|
||||||
|
{
|
||||||
|
// Wait for input from user
|
||||||
|
if (!m_system->ReadMapping(mapping, 50, false, READ_KEYBOARD|READ_MERGE, uiExit->GetMapping()))
|
||||||
|
{
|
||||||
|
// If user pressed aborted input, then undo all changes and finish configuration
|
||||||
|
index = 0;
|
||||||
|
for (it = toConfigure.begin(); it < toConfigure.end(); it++)
|
||||||
|
{
|
||||||
|
(*it)->SetMapping(oldMappings[index].c_str());
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
puts("");
|
||||||
|
|
||||||
|
m_system->ConfigEnd();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stricmp(mapping, "KEY_RETURN") == 0 || stricmp(mapping, "KEY_S") == 0)
|
||||||
|
{
|
||||||
|
// Set the input mapping
|
||||||
|
printf("Setting...");
|
||||||
|
fflush(stdout); // required on terminals that use buffering
|
||||||
|
if (input->Configure(false, uiExit->GetMapping()))
|
||||||
|
{
|
||||||
|
printf(" %s\n", input->GetMapping());
|
||||||
|
if (stricmp(mapping, "KEY_RETURN") == 0)
|
||||||
|
index++;
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf(" [Cancelled]\n");
|
||||||
|
goto Redisplay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (stricmp(mapping, "KEY_A") == 0)
|
||||||
|
{
|
||||||
|
// Append to the input mapping(s)
|
||||||
|
printf("Appending...");
|
||||||
|
fflush(stdout); // required on terminals that use buffering
|
||||||
|
if (input->Configure(true, uiExit->GetMapping()))
|
||||||
|
printf(" %s\n", input->GetMapping());
|
||||||
|
else
|
||||||
|
printf(" [Cancelled]\n");
|
||||||
|
goto Redisplay;
|
||||||
|
}
|
||||||
|
else if (stricmp(mapping, "KEY_C") == 0)
|
||||||
|
{
|
||||||
|
// Clear the input mapping(s)
|
||||||
|
input->SetMapping("NONE");
|
||||||
|
printf("Cleared\n");
|
||||||
|
goto Redisplay;
|
||||||
|
}
|
||||||
|
else if (stricmp(mapping, "KEY_R") == 0)
|
||||||
|
{
|
||||||
|
// Reset the input mapping(s) to the default
|
||||||
|
input->ResetToDefaultMapping();
|
||||||
|
printf("Reset\n");
|
||||||
|
goto Redisplay;
|
||||||
|
}
|
||||||
|
else if (stricmp(mapping, "KEY_DOWN") == 0)
|
||||||
|
{
|
||||||
|
// Move forward to the next mapping
|
||||||
|
puts("");
|
||||||
|
index++;
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
else if (stricmp(mapping, "KEY_UP") == 0)
|
||||||
|
{
|
||||||
|
// Move back to the previous mapping
|
||||||
|
if (index > 0)
|
||||||
|
{
|
||||||
|
puts("");
|
||||||
|
index--;
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (stricmp(mapping, "KEY_HOME") == 0)
|
||||||
|
{
|
||||||
|
// Move to first input
|
||||||
|
puts("");
|
||||||
|
index = 0;
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
else if (stricmp(mapping, "KEY_H") == 0)
|
||||||
|
{
|
||||||
|
// Print the help message again
|
||||||
|
puts("");
|
||||||
|
PrintConfigureInputsHelp();
|
||||||
|
goto Redisplay;
|
||||||
|
}
|
||||||
|
else if (stricmp(mapping, "KEY_Q") == 0)
|
||||||
|
{
|
||||||
|
// Finish configuration
|
||||||
|
puts("");
|
||||||
|
|
||||||
|
m_system->ConfigEnd();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// All inputs set, finish configuration
|
||||||
|
puts("");
|
||||||
|
|
||||||
|
m_system->ConfigEnd();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CInputs::PrintInputs(const GameInfo *game)
|
||||||
|
{
|
||||||
|
// Print header
|
||||||
|
int gameFlags;
|
||||||
|
if (game != NULL)
|
||||||
|
{
|
||||||
|
PrintHeader("Input Assignments for %s", game->title);
|
||||||
|
gameFlags = game->inputFlags;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PrintHeader("Input Assignments");
|
||||||
|
gameFlags = GAME_INPUT_ALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *groupLabel = NULL;
|
||||||
|
|
||||||
|
for (vector<CInput*>::iterator it = m_inputs.begin(); it < m_inputs.end(); it++)
|
||||||
|
{
|
||||||
|
if (!InputIsConfigurable(*it) || !((*it)->gameFlags & gameFlags))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const char *itGroupLabel = (*it)->GetInputGroup();
|
||||||
|
if (groupLabel == NULL || stricmp(groupLabel, itGroupLabel) != 0)
|
||||||
|
{
|
||||||
|
groupLabel = itGroupLabel;
|
||||||
|
printf("%s:\n", groupLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(" %s = %s\n", (*it)->label, (*it)->GetMapping());
|
||||||
|
}
|
||||||
|
|
||||||
|
puts("");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CInputs::Poll(const GameInfo *game, unsigned dispX, unsigned dispY, unsigned dispW, unsigned dispH)
|
||||||
|
{
|
||||||
|
// Update the input system with the current display geometry
|
||||||
|
m_system->SetDisplayGeom(dispX, dispY, dispW, dispH);
|
||||||
|
|
||||||
|
// Poll the input system
|
||||||
|
if (!m_system->Poll())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Poll all UI inputs and all the inputs used by the current game, or all inputs if game is NULL
|
||||||
|
int gameFlags = (game != NULL ? game->inputFlags : GAME_INPUT_ALL);
|
||||||
|
for (vector<CInput*>::iterator it = m_inputs.begin(); it < m_inputs.end(); it++)
|
||||||
|
{
|
||||||
|
if ((*it)->gameFlags == UI_INPUT || (*it)->gameFlags & gameFlags)
|
||||||
|
(*it)->Poll();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CInputs::DumpState(const GameInfo *game)
|
||||||
|
{
|
||||||
|
// Print header
|
||||||
|
int gameFlags;
|
||||||
|
if (game != NULL)
|
||||||
|
{
|
||||||
|
PrintHeader("Input States for %s", game->title);
|
||||||
|
gameFlags = game->inputFlags;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PrintHeader("Input States");
|
||||||
|
gameFlags = GAME_INPUT_ALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop through the inputs used by the current game, or all inputs if game is NULL, and dump their values to stdout
|
||||||
|
for (vector<CInput*>::iterator it = m_inputs.begin(); it < m_inputs.end(); it++)
|
||||||
|
{
|
||||||
|
if (!((*it)->gameFlags & gameFlags))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (InputIsConfigurable(*it))
|
||||||
|
printf("%s [%s] = (%d)\n", (*it)->id, (*it)->GetMapping(), (*it)->value);
|
||||||
|
else
|
||||||
|
printf("%s = (%d)\n", (*it)->id, (*it)->value);
|
||||||
|
}
|
||||||
|
}
|
195
Inputs/Inputs.h
Normal file
195
Inputs/Inputs.h
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
#ifndef INCLUDED_INPUTS_H
|
||||||
|
#define INCLUDED_INPUTS_H
|
||||||
|
|
||||||
|
#include "Types.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
class CInputSystem;
|
||||||
|
class CInput;
|
||||||
|
class CAnalogInput;
|
||||||
|
class CAxisInput;
|
||||||
|
class CSwitchInput;
|
||||||
|
class CGearShift4Input;
|
||||||
|
class CINIFile;
|
||||||
|
struct GameInfo;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Represents the collection of Model3 inputs.
|
||||||
|
*/
|
||||||
|
class CInputs
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// Assigned input system
|
||||||
|
CInputSystem *m_system;
|
||||||
|
|
||||||
|
// Vector of all created inputs
|
||||||
|
vector<CInput*> m_inputs;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Adds a switch input (eg button) to this collection.
|
||||||
|
*/
|
||||||
|
CSwitchInput* AddSwitchInput(const char *id, const char *label, unsigned gameFlags, const char *defaultMapping,
|
||||||
|
UINT16 offVal = 0x00, UINT16 onVal = 0x01);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Adds an analog input (eg pedal) to this collection.
|
||||||
|
*/
|
||||||
|
CAnalogInput* AddAnalogInput(const char *id, const char *label, unsigned gameFlags, const char *defaultMapping,
|
||||||
|
UINT16 minVal = 0x00, UINT16 maxVal = 0xFF);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Adds an axis input (eg jostick axis, light gun axis or steering wheel) to this collection.
|
||||||
|
*/
|
||||||
|
CAxisInput* AddAxisInput(const char *id, const char *label, unsigned gameFlags, const char *defaultMapping,
|
||||||
|
CAnalogInput* axisNeg, CAnalogInput* axisPos, UINT16 minVal = 0x00, UINT16 offVal = 0x80, UINT16 maxVal = 0xFF);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Adds a 4-gear shifter input to this collection.
|
||||||
|
*/
|
||||||
|
CGearShift4Input* AddGearShift4Input(const char *id, const char *label, unsigned gameFlags,
|
||||||
|
CSwitchInput *shift1, CSwitchInput *shift2, CSwitchInput *shift3, CSwitchInput *shift4, CSwitchInput *shiftUp, CSwitchInput *shiftDown);
|
||||||
|
|
||||||
|
void PrintHeader(const char *fmt, ...);
|
||||||
|
|
||||||
|
void PrintConfigureInputsHelp();
|
||||||
|
|
||||||
|
public:
|
||||||
|
// UI controls
|
||||||
|
CSwitchInput* uiExit;
|
||||||
|
CSwitchInput* uiReset;
|
||||||
|
CSwitchInput* uiPause;
|
||||||
|
CSwitchInput* uiSaveState;
|
||||||
|
CSwitchInput* uiChangeSlot;
|
||||||
|
CSwitchInput* uiLoadState;
|
||||||
|
CSwitchInput* uiDumpInpState;
|
||||||
|
CSwitchInput* uiClearNVRAM;
|
||||||
|
CSwitchInput* uiToggleCursor;
|
||||||
|
CSwitchInput* uiToggleFrLimit;
|
||||||
|
|
||||||
|
// Common controls between all games
|
||||||
|
CSwitchInput* coin[2];
|
||||||
|
CSwitchInput* start[2];
|
||||||
|
CSwitchInput* test[2];
|
||||||
|
CSwitchInput* service[2];
|
||||||
|
|
||||||
|
// Joysticks (players 1 and 2)
|
||||||
|
CSwitchInput* up[2];
|
||||||
|
CSwitchInput* down[2];
|
||||||
|
CSwitchInput* left[2];
|
||||||
|
CSwitchInput* right[2];
|
||||||
|
|
||||||
|
// Fighting game controls (players 1 and 2)
|
||||||
|
CSwitchInput* punch[2];
|
||||||
|
CSwitchInput* kick[2];
|
||||||
|
CSwitchInput* guard[2];
|
||||||
|
CSwitchInput* escape[2];
|
||||||
|
|
||||||
|
// Soccer game controls (players 1 and 2)
|
||||||
|
CSwitchInput* shortPass[2];
|
||||||
|
CSwitchInput* longPass[2];
|
||||||
|
CSwitchInput* shoot[2];
|
||||||
|
|
||||||
|
// Vehicle controls
|
||||||
|
CAxisInput* steering;
|
||||||
|
CAnalogInput* accelerator;
|
||||||
|
CAnalogInput* brake;
|
||||||
|
|
||||||
|
// VR view buttons: VR1 Red, VR2 Blue, VR3 Yellow, VR4 Green
|
||||||
|
CSwitchInput* vr[4];
|
||||||
|
|
||||||
|
// 4-speed gear shift
|
||||||
|
CGearShift4Input* gearShift4;
|
||||||
|
|
||||||
|
// Rally controls
|
||||||
|
CSwitchInput* viewChange;
|
||||||
|
CSwitchInput* handBrake;
|
||||||
|
|
||||||
|
// Twin joysticks
|
||||||
|
CSwitchInput* twinJoyTurnLeft;
|
||||||
|
CSwitchInput* twinJoyTurnRight;
|
||||||
|
CSwitchInput* twinJoyStrafeLeft;
|
||||||
|
CSwitchInput* twinJoyStrafeRight;
|
||||||
|
CSwitchInput* twinJoyForward;
|
||||||
|
CSwitchInput* twinJoyReverse;
|
||||||
|
CSwitchInput* twinJoyJump;
|
||||||
|
CSwitchInput* twinJoyCrouch;
|
||||||
|
CSwitchInput* twinJoyLeftShot;
|
||||||
|
CSwitchInput* twinJoyRightShot;
|
||||||
|
CSwitchInput* twinJoyLeftTurbo;
|
||||||
|
CSwitchInput* twinJoyRightTurbo;
|
||||||
|
|
||||||
|
// Analog joystick
|
||||||
|
CAxisInput* analogJoyX;
|
||||||
|
CAxisInput* analogJoyY;
|
||||||
|
CSwitchInput* analogJoyTrigger;
|
||||||
|
CSwitchInput* analogJoyEvent;
|
||||||
|
|
||||||
|
// Gun controls (players 1 and 2)
|
||||||
|
CAxisInput* gunX[2];
|
||||||
|
CAxisInput* gunY[2];
|
||||||
|
CSwitchInput* trigger[2];
|
||||||
|
CSwitchInput* offscreen[2];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Creates a set of inputs with the given input system.
|
||||||
|
*/
|
||||||
|
CInputs(CInputSystem *system);
|
||||||
|
|
||||||
|
~CInputs();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the assigned input system.
|
||||||
|
*/
|
||||||
|
CInputSystem *GetInputSystem();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initializes the inputs. Must be called before any other methods are used.
|
||||||
|
*/
|
||||||
|
bool Initialize();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Looks up an input by its identifier.
|
||||||
|
*/
|
||||||
|
CInput* LookupInputByID(const char* id);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns true if the given input is configurable and can be set by the user.
|
||||||
|
*/
|
||||||
|
bool InputIsConfigurable(CInput *input);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reads the input mapping assignments from the given INI file.
|
||||||
|
*/
|
||||||
|
void ReadFromINIFile(CINIFile *ini, const char *section);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Writes the current input mapping assignments to the given INI file.
|
||||||
|
*/
|
||||||
|
void WriteToINIFile(CINIFile *ini, const char *section);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Configures the current input mapping assignments for the given game, or all inputs if game is NULL, by asking the user for input.
|
||||||
|
* Returns true if the inputs were configured okay or false if the user exited without requesting to save changes.
|
||||||
|
*/
|
||||||
|
bool ConfigureInputs(const GameInfo *game, unsigned dispX, unsigned dispY, unsigned dispW, unsigned dispH);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prints to stdout the current input mapping assignments for the given game, or all inputs if game is NULL.
|
||||||
|
*/
|
||||||
|
void PrintInputs(const GameInfo *game);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Polls (updates) the inputs for the given game, or all inputs if game is NULL, updating their values from their respective input sources.
|
||||||
|
* First the input system is polled (CInputSystem.Poll()) and then each input is polled (CInput.Poll()).
|
||||||
|
*/
|
||||||
|
bool Poll(const GameInfo *game, unsigned dispX, unsigned dispY, unsigned dispW, unsigned dispH);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prints the current values of the inputs for the given game, or all inputs if game is NULL, to stdout for debugging purposes.
|
||||||
|
*/
|
||||||
|
void DumpState(const GameInfo *game);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // INCLUDED_INPUTS_H
|
117
Inputs/MultiInputSource.cpp
Normal file
117
Inputs/MultiInputSource.cpp
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
#include "Supermodel.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
ESourceType CMultiInputSource::GetCombinedType(vector<CInputSource*> &sources)
|
||||||
|
{
|
||||||
|
// Check if vector is empty
|
||||||
|
if (sources.size() == 0)
|
||||||
|
return SourceEmpty;
|
||||||
|
// Otherwise, see whether all sources are switches, or if have a full- or half-axis present
|
||||||
|
bool allSwitches = true;
|
||||||
|
bool hasFullAxis = false;
|
||||||
|
bool hasHalfAxis = false;
|
||||||
|
for (vector<CInputSource*>::iterator it = sources.begin(); it < sources.end(); it++)
|
||||||
|
{
|
||||||
|
if ((*it)->type == SourceInvalid)
|
||||||
|
return SourceInvalid; // An invalid source makes the whole lot invalid
|
||||||
|
else if ((*it)->type == SourceSwitch)
|
||||||
|
continue;
|
||||||
|
allSwitches = false;
|
||||||
|
if ((*it)->type == SourceFullAxis)
|
||||||
|
{
|
||||||
|
if (hasHalfAxis)
|
||||||
|
return SourceInvalid; // A half-axis and full-axis combined makes the whole lot invalid
|
||||||
|
hasFullAxis = true;
|
||||||
|
}
|
||||||
|
else if ((*it)->type == SourceHalfAxis)
|
||||||
|
{
|
||||||
|
if (hasFullAxis)
|
||||||
|
return SourceInvalid; // A half-axis and full-axis combined makes the whole lot invalid
|
||||||
|
hasHalfAxis = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Return resulting combined type
|
||||||
|
if (allSwitches) return SourceSwitch;
|
||||||
|
else if (hasFullAxis) return SourceFullAxis;
|
||||||
|
else if (hasHalfAxis) return SourceHalfAxis;
|
||||||
|
else return SourceEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
CMultiInputSource::CMultiInputSource() : CInputSource(SourceEmpty), m_isOr(true), m_numSrcs(0), m_srcArray(NULL) { }
|
||||||
|
|
||||||
|
CMultiInputSource::CMultiInputSource(bool isOr, vector<CInputSource*> &sources) :
|
||||||
|
CInputSource(GetCombinedType(sources)), m_isOr(isOr), m_numSrcs(sources.size())
|
||||||
|
{
|
||||||
|
m_srcArray = new CInputSource*[m_numSrcs];
|
||||||
|
copy(sources.begin(), sources.end(), m_srcArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
CMultiInputSource::~CMultiInputSource()
|
||||||
|
{
|
||||||
|
if (m_srcArray != NULL)
|
||||||
|
delete m_srcArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CMultiInputSource::GetValueAsSwitch(bool &val)
|
||||||
|
{
|
||||||
|
if (m_isOr)
|
||||||
|
{
|
||||||
|
// Return value for first input that is active
|
||||||
|
for (int i = 0; i < m_numSrcs; i++)
|
||||||
|
{
|
||||||
|
if (m_srcArray[i]->GetValueAsSwitch(val))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Check all switch inputs are active
|
||||||
|
for (int i = 0; i < m_numSrcs; i++)
|
||||||
|
{
|
||||||
|
if (m_srcArray[i]->type == SourceSwitch && !m_srcArray[i]->GetValueAsSwitch(val))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Then return value for first non-switch input that is active
|
||||||
|
for (int i = 0; i < m_numSrcs; i++)
|
||||||
|
{
|
||||||
|
if (m_srcArray[i]->type != SourceSwitch && m_srcArray[i]->GetValueAsSwitch(val))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Otherwise, value is only valid if not empty and all inputs are switches
|
||||||
|
return m_numSrcs > 0 && type == SourceSwitch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CMultiInputSource::GetValueAsAnalog(int &val, int minVal, int offVal, int maxVal)
|
||||||
|
{
|
||||||
|
if (m_isOr)
|
||||||
|
{
|
||||||
|
// Return value for first input that is active
|
||||||
|
for (int i = 0; i < m_numSrcs; i++)
|
||||||
|
{
|
||||||
|
if (m_srcArray[i]->GetValueAsAnalog(val, minVal, offVal, maxVal))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Check all switch inputs are active
|
||||||
|
for (int i = 0; i < m_numSrcs; i++)
|
||||||
|
{
|
||||||
|
if (m_srcArray[i]->type == SourceSwitch && !m_srcArray[i]->GetValueAsAnalog(val, minVal, offVal, maxVal))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Then return value for first non-switch input that is active
|
||||||
|
for (int i = 0; i < m_numSrcs; i++)
|
||||||
|
{
|
||||||
|
if (m_srcArray[i]->type != SourceSwitch && m_srcArray[i]->GetValueAsAnalog(val, minVal, offVal, maxVal))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Otherwise, value is only valid if not empty and all inputs are switches
|
||||||
|
return m_numSrcs > 0 && type == SourceSwitch;
|
||||||
|
}
|
||||||
|
}
|
51
Inputs/MultiInputSource.h
Normal file
51
Inputs/MultiInputSource.h
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
#ifndef INCLUDED_MULTIINPUTSOURCE_H
|
||||||
|
#define INCLUDED_MULTIINPUTSOURCE_H
|
||||||
|
|
||||||
|
#include "InputSource.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
using namespace std;
|
||||||
|
/*
|
||||||
|
* Represents a collection of input sources and combines their values into a single value.
|
||||||
|
* When multiple mappings are assigned to an input, this is the input source that is created.
|
||||||
|
*/
|
||||||
|
class CMultiInputSource : public CInputSource
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
// Controls how the inputs sources are combined
|
||||||
|
bool m_isOr;
|
||||||
|
|
||||||
|
// Number of input sources (if zero then represents an 'empty' source)
|
||||||
|
int m_numSrcs;
|
||||||
|
|
||||||
|
// Array of the input sources
|
||||||
|
CInputSource **m_srcArray;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
* Returns the combined source type of the given vector of sources.
|
||||||
|
*/
|
||||||
|
static ESourceType GetCombinedType(vector<CInputSource*> &sources);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Constructs an 'empty' source (ie one which is always 'off').
|
||||||
|
*/
|
||||||
|
CMultiInputSource();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Constructs a multiple input source from the given vector of sources.
|
||||||
|
* If isOr is true, then the value of this input will always be the value of the first active input found. If false, then all
|
||||||
|
* switch inputs must be active for this input to have a value (which will be the value of the first non-switch input in the list,
|
||||||
|
* or the first switch input if there are none).
|
||||||
|
*/
|
||||||
|
CMultiInputSource(bool isOr, vector<CInputSource*> &sources);
|
||||||
|
|
||||||
|
~CMultiInputSource();
|
||||||
|
|
||||||
|
bool GetValueAsSwitch(bool &val);
|
||||||
|
|
||||||
|
bool GetValueAsAnalog(int &val, int minVal, int offVal, int maxVal);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // INCLUDED_MULTIINPUTSOURCE_H
|
506
Model3/53C810.cpp
Normal file
506
Model3/53C810.cpp
Normal file
|
@ -0,0 +1,506 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 53C810.cpp
|
||||||
|
*
|
||||||
|
* Implementation of the C53C810 class: NCR 53C810 SCSI controller.
|
||||||
|
*
|
||||||
|
* TO-DO List:
|
||||||
|
* -----------
|
||||||
|
* - VF3 does something weird: it writes DSP (triggering automatic code
|
||||||
|
* execution because MAN=0) and THEN sets single step mode, expecting an
|
||||||
|
* interrupt to occur. I suspect this is incorrect operation and that
|
||||||
|
* the SCRIPTS processor either enters single-step mode while the memory
|
||||||
|
* transfer is underway (though it is unlikely because it's such a short
|
||||||
|
* transfer), or that single step can occur even when the device is "halted"
|
||||||
|
* (which would mean the SCRIPTS processor executes an invalid instruction
|
||||||
|
* once or twice without the VF3 SCSI driver noticing). Unfortunately, the
|
||||||
|
* former is not feasible to emulate. If automatic SCRIPTS execution is
|
||||||
|
* disabled when single-stepping is enabled, Scud Race breaks (glitchy,
|
||||||
|
* jerky graphics). Enabling automatic execution and also allowing single
|
||||||
|
* stepping to occur when the processor is halted seems to work, but it
|
||||||
|
* causes invalid instructions to be hit each time.
|
||||||
|
* - Check to ensure the invalid instructions hit above would never be decoded
|
||||||
|
* as real SCRIPTS instructions (in some cases, appears they can be...)
|
||||||
|
* - Is the SIP bit supposed to be set after single stepping?
|
||||||
|
* - pg 2-22 (42) of the manual has description of how to clear interrupts.
|
||||||
|
* - Another way to fix VF3 is to just set the single-step and DMA interrupt
|
||||||
|
* flags at all times.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "Supermodel.h"
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Save States
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void C53C810::SaveState(CBlockFile *SaveState)
|
||||||
|
{
|
||||||
|
SaveState->NewBlock("53C810", __FILE__);
|
||||||
|
SaveState->Write(Ctx.regs, sizeof(Ctx.regs));
|
||||||
|
SaveState->Write(&Ctx.regTEMP, sizeof(Ctx.regTEMP));
|
||||||
|
SaveState->Write(&Ctx.regDSP, sizeof(Ctx.regDSP));
|
||||||
|
SaveState->Write(&Ctx.regDSPS, sizeof(Ctx.regDSPS));
|
||||||
|
SaveState->Write(&Ctx.regDBC, sizeof(Ctx.regDBC));
|
||||||
|
SaveState->Write(&Ctx.regDCMD, sizeof(Ctx.regDCMD));
|
||||||
|
SaveState->Write(&Ctx.regDCNTL, sizeof(Ctx.regDCNTL));
|
||||||
|
SaveState->Write(&Ctx.regDMODE, sizeof(Ctx.regDMODE));
|
||||||
|
SaveState->Write(&Ctx.regDSTAT, sizeof(Ctx.regDSTAT));
|
||||||
|
SaveState->Write(&Ctx.regISTAT, sizeof(Ctx.regISTAT));
|
||||||
|
}
|
||||||
|
|
||||||
|
void C53C810::LoadState(CBlockFile *SaveState)
|
||||||
|
{
|
||||||
|
if (OKAY != SaveState->FindBlock("53C810"))
|
||||||
|
{
|
||||||
|
ErrorLog("Unable to load 53C810 state. Save state file is corrupted.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveState->Read(Ctx.regs, sizeof(Ctx.regs));
|
||||||
|
SaveState->Read(&Ctx.regTEMP, sizeof(Ctx.regTEMP));
|
||||||
|
SaveState->Read(&Ctx.regDSP, sizeof(Ctx.regDSP));
|
||||||
|
SaveState->Read(&Ctx.regDSPS, sizeof(Ctx.regDSPS));
|
||||||
|
SaveState->Read(&Ctx.regDBC, sizeof(Ctx.regDBC));
|
||||||
|
SaveState->Read(&Ctx.regDCMD, sizeof(Ctx.regDCMD));
|
||||||
|
SaveState->Read(&Ctx.regDCNTL, sizeof(Ctx.regDCNTL));
|
||||||
|
SaveState->Read(&Ctx.regDMODE, sizeof(Ctx.regDMODE));
|
||||||
|
SaveState->Read(&Ctx.regDSTAT, sizeof(Ctx.regDSTAT));
|
||||||
|
SaveState->Read(&Ctx.regISTAT, sizeof(Ctx.regISTAT));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
SCRIPTS Emulation
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
static inline UINT32 Fetch(struct NCR53C810Context *Ctx)
|
||||||
|
{
|
||||||
|
UINT32 data = Ctx->Bus->Read32(Ctx->regDSP);
|
||||||
|
Ctx->regDSP += 4;
|
||||||
|
return FLIPENDIAN32(data); // remember: bus is big endian, need to convert to little endian
|
||||||
|
}
|
||||||
|
|
||||||
|
//TO-DO: check if this ever occurs in single-step mode (if so, we would need to stack interrupts)
|
||||||
|
static BOOL SCRIPTS_Int_IntFly(struct NCR53C810Context *Ctx)
|
||||||
|
{
|
||||||
|
Ctx->halt = TRUE; // halt SCRIPTS execution
|
||||||
|
Ctx->regISTAT |= 1; // DMA interrupt pending
|
||||||
|
Ctx->regDSTAT |= 4; // SCRIPTS interrupt instruction received
|
||||||
|
Ctx->IRQ->Assert(Ctx->scsiIRQ);
|
||||||
|
|
||||||
|
if ((Ctx->regDBC&0x100000)) // INTFLY
|
||||||
|
return ErrorLog("53C810 INTFLY instruction not emulated!");
|
||||||
|
return OKAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL SCRIPTS_MoveMemory(struct NCR53C810Context *Ctx)
|
||||||
|
{
|
||||||
|
UINT32 src, dest;
|
||||||
|
unsigned numBytes, i;
|
||||||
|
|
||||||
|
// Get operands
|
||||||
|
src = Ctx->regDSPS;
|
||||||
|
dest = Ctx->regTEMP = Fetch(Ctx);
|
||||||
|
numBytes = Ctx->regDBC;
|
||||||
|
// not implemented: illegal instruction interrupt when src and dest are not aligned the same way
|
||||||
|
|
||||||
|
DebugLog("53C810: Move Memory %08X -> %08X, %X\n", src, dest, numBytes);
|
||||||
|
|
||||||
|
// Perform a 32-bit copy if possible
|
||||||
|
for (i = 0; i < (numBytes/4); i++)
|
||||||
|
{
|
||||||
|
Ctx->Bus->Write32(dest, Ctx->Bus->Read32(src));
|
||||||
|
dest += 4;
|
||||||
|
src += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finish off the last few odd bytes
|
||||||
|
numBytes &= 3;
|
||||||
|
while (numBytes)
|
||||||
|
{
|
||||||
|
Ctx->Bus->Write8(dest++, Ctx->Bus->Read8(src++));
|
||||||
|
--numBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update registers
|
||||||
|
Ctx->regDBC = 0;
|
||||||
|
Ctx->regDSPS = src;
|
||||||
|
Ctx->regTEMP = dest;
|
||||||
|
|
||||||
|
return OKAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invalid instruction handler
|
||||||
|
static BOOL SCRIPTS_Invalid(struct NCR53C810Context *Ctx)
|
||||||
|
{
|
||||||
|
DebugLog("53C810 encountered an unrecognized instruction (%02X%06X, DSP=%08X)\n!", Ctx->regDCMD, Ctx->regDBC, Ctx->regDSP);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void C53C810::Run(BOOL singleStep)
|
||||||
|
{
|
||||||
|
UINT32 op;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (singleStep)// && !Ctx.halt)
|
||||||
|
{
|
||||||
|
// Fetch instruction (first two words are always fetched)
|
||||||
|
op = Fetch(&Ctx); // word 1
|
||||||
|
Ctx.regDBC = op&0x00FFFFFF;
|
||||||
|
Ctx.regDCMD = (op>>24)&0xFF;
|
||||||
|
Ctx.regDSPS = Fetch(&Ctx); // word 2
|
||||||
|
|
||||||
|
// Single step
|
||||||
|
OpTable[Ctx.regDCMD](&Ctx);
|
||||||
|
|
||||||
|
// Issue IRQ and finish
|
||||||
|
Ctx.regISTAT |= 3; // DMA interrupt pending (NOTE: should SIP be set? I don't think so...)
|
||||||
|
Ctx.regDSTAT |= 8; // single step interrupt
|
||||||
|
Ctx.IRQ->Assert(Ctx.scsiIRQ); // generate an interrupt
|
||||||
|
DebugLog("53C810: Asserted IRQ\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Automatic mode: run (as long as the processor is not halted)
|
||||||
|
for (i = 0; (i < 100) && !Ctx.halt; i++)
|
||||||
|
{
|
||||||
|
// Fetch instruction (first two words are always fetched)
|
||||||
|
op = Fetch(&Ctx); // word 1
|
||||||
|
Ctx.regDBC = op&0x00FFFFFF;
|
||||||
|
Ctx.regDCMD = (op>>24)&0xFF;
|
||||||
|
Ctx.regDSPS = Fetch(&Ctx); // word 2
|
||||||
|
|
||||||
|
// Execute!
|
||||||
|
if (OpTable[Ctx.regDCMD](&Ctx) != OKAY)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert instructions into the LUT under control of the mask
|
||||||
|
void C53C810::Insert(UINT8 mask, UINT8 op, BOOL (*Handler)(struct NCR53C810Context *))
|
||||||
|
{
|
||||||
|
UINT32 i;
|
||||||
|
|
||||||
|
for (i = 0; i < 256; i++)
|
||||||
|
{
|
||||||
|
if ((i&mask) == op)
|
||||||
|
OpTable[i] = Handler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void C53C810::BuildOpTable(void)
|
||||||
|
{
|
||||||
|
Insert(0, 0, &SCRIPTS_Invalid);
|
||||||
|
Insert(0xE0, 0xC0, &SCRIPTS_MoveMemory);
|
||||||
|
Insert(0xF8, 0x98, &SCRIPTS_Int_IntFly);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Register and PCI Access Handlers
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void C53C810::WriteRegister(unsigned reg, UINT8 data)
|
||||||
|
{
|
||||||
|
if (reg >= 0x60)
|
||||||
|
{
|
||||||
|
ErrorLog("%s:%s: Invalid 53C810 register (%02X).", __FILE__, __LINE__, reg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugLog("53C810 write: %02X=%02X (PC=%08X, LR=%08X)\n", reg, data, ppc_get_pc(), ppc_get_lr());
|
||||||
|
|
||||||
|
// Dump everything into the register file
|
||||||
|
Ctx.regs[reg&0xFF] = data;
|
||||||
|
|
||||||
|
// Do something extra with the ones that we actually care about
|
||||||
|
// TO-DO: prevent invalid/reserved/read-only registers from being written?
|
||||||
|
switch(reg)
|
||||||
|
{
|
||||||
|
case 0x14: // ISTAT
|
||||||
|
Ctx.regISTAT = data;
|
||||||
|
DebugLog("ISTAT=%02X\n", data);
|
||||||
|
break;
|
||||||
|
case 0x1C: // TEMP 7-0
|
||||||
|
Ctx.regTEMP &= 0xFFFFFF00;
|
||||||
|
Ctx.regTEMP |= data;
|
||||||
|
break;
|
||||||
|
case 0x1D: // TEMP 15-8
|
||||||
|
Ctx.regTEMP &= 0xFFFF00FF;
|
||||||
|
Ctx.regTEMP |= (data<<8);
|
||||||
|
break;
|
||||||
|
case 0x1E: // TEMP 23-16
|
||||||
|
Ctx.regTEMP &= 0xFF00FFFF;
|
||||||
|
Ctx.regTEMP |= (data<<16);
|
||||||
|
break;
|
||||||
|
case 0x1F: // TEMP 31-24
|
||||||
|
Ctx.regTEMP &= 0x00FFFFFF;
|
||||||
|
Ctx.regTEMP |= (data<<24);
|
||||||
|
break;
|
||||||
|
case 0x24: // DBC 7-0
|
||||||
|
Ctx.regDBC &= 0xFFFFFF00;
|
||||||
|
Ctx.regDBC |= data;
|
||||||
|
break;
|
||||||
|
case 0x25: // DBC 15-8
|
||||||
|
Ctx.regDBC &= 0xFFFF00FF;
|
||||||
|
Ctx.regDBC |= (data<<8);
|
||||||
|
break;
|
||||||
|
case 0x26: // DBC 23-16
|
||||||
|
Ctx.regDBC &= 0xFF00FFFF;
|
||||||
|
Ctx.regDBC |= (data<<16);
|
||||||
|
break;
|
||||||
|
case 0x27: // DCMD
|
||||||
|
Ctx.regDCMD = data;
|
||||||
|
break;
|
||||||
|
case 0x2C: // DSP 7-0
|
||||||
|
Ctx.regDSP &= 0xFFFFFF00;
|
||||||
|
Ctx.regDSP |= data;
|
||||||
|
break;
|
||||||
|
case 0x2D: // DSP 15-8
|
||||||
|
Ctx.regDSP &= 0xFFFF00FF;
|
||||||
|
Ctx.regDSP |= (data<<8);
|
||||||
|
break;
|
||||||
|
case 0x2E: // DSP 23-16
|
||||||
|
Ctx.regDSP &= 0xFF00FFFF;
|
||||||
|
Ctx.regDSP |= (data<<16);
|
||||||
|
break;
|
||||||
|
case 0x2F: // DSP 31-24
|
||||||
|
Ctx.regDSP &= 0x00FFFFFF;
|
||||||
|
Ctx.regDSP |= (data<<24);
|
||||||
|
Ctx.halt = FALSE; // writing this register un-halts 53C810 operation (pg.6-31 of LSI manual)
|
||||||
|
if (!(Ctx.regDMODE&1)) // if MAN=0, start SCRIPTS automatically
|
||||||
|
// To-Do: is this correct? Should single step really be tested first?
|
||||||
|
//if (!(Ctx.regDCNTL&0x10) && !(Ctx.regDMODE&1)) // if MAN=0 and not single stepping, start SCRIPTS automatically
|
||||||
|
{
|
||||||
|
DebugLog("53C810: Automatically starting (PC=%08X, LR=%08X, single step=%d)\n", ppc_get_pc(), ppc_get_lr(), !!(Ctx.regDCNTL&0x10));
|
||||||
|
Run(FALSE); // automatic
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x30: // DSPS 7-0
|
||||||
|
Ctx.regDSPS &= 0xFFFFFF00;
|
||||||
|
Ctx.regDSPS |= data;
|
||||||
|
break;
|
||||||
|
case 0x31: // DSPS 15-8
|
||||||
|
Ctx.regDSPS &= 0xFFFF00FF;
|
||||||
|
Ctx.regDSPS |= (data<<8);
|
||||||
|
break;
|
||||||
|
case 0x32: // DSPS 23-16
|
||||||
|
Ctx.regDSPS &= 0xFF00FFFF;
|
||||||
|
Ctx.regDSPS |= (data<<16);
|
||||||
|
break;
|
||||||
|
case 0x33: // DSPS 31-24
|
||||||
|
Ctx.regDSPS &= 0x00FFFFFF;
|
||||||
|
Ctx.regDSPS |= (data<<24);
|
||||||
|
break;
|
||||||
|
case 0x38: // DMODE
|
||||||
|
Ctx.regDMODE = data;
|
||||||
|
break;
|
||||||
|
case 0x3B: // DCNTL
|
||||||
|
Ctx.regDCNTL = data;
|
||||||
|
if ((Ctx.regDCNTL&0x14) == 0x14) // single step
|
||||||
|
{
|
||||||
|
DebugLog("53C810: single step: %08X, (halt=%d)\n", Ctx.regDSP, Ctx.halt);
|
||||||
|
Run(TRUE);
|
||||||
|
}
|
||||||
|
else if ((Ctx.regDCNTL&0x04)) // start DMA bit
|
||||||
|
{
|
||||||
|
DebugLog("53C810: Manually starting\n");
|
||||||
|
Run(FALSE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT8 C53C810::ReadRegister(unsigned reg)
|
||||||
|
{
|
||||||
|
UINT8 ret;
|
||||||
|
|
||||||
|
if (reg >= 0x60)
|
||||||
|
{
|
||||||
|
ErrorLog("%s:%s: Invalid 53C810 register (%02X).", __FILE__, __LINE__, reg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugLog("53C810 read: %02X (PC=%08X, LR=%08X)\n", reg, ppc_get_pc(), ppc_get_lr());
|
||||||
|
|
||||||
|
// Some registers require special handling
|
||||||
|
switch(reg)
|
||||||
|
{
|
||||||
|
case 0x0C: // DSTAT
|
||||||
|
// For now, we don't generate stacked interrupts, so always clear IRQ status
|
||||||
|
ret = Ctx.regDSTAT;
|
||||||
|
//TO-DO: manual says these should be cleared here but MAME never clears them. What's up with that?
|
||||||
|
Ctx.regISTAT &= 0xFE; // clear DIP bit (DMA interrupt)
|
||||||
|
Ctx.regDSTAT &= 0xF7; // clear SSI (single step interrupt)
|
||||||
|
//Ctx.regISTAT |= 1; // doing this is another way to fix VF3
|
||||||
|
//Ctx.regDSTAT |= 8;
|
||||||
|
Ctx.IRQ->Deassert(Ctx.scsiIRQ);
|
||||||
|
//DebugLog("53C810: DSTAT read\n");
|
||||||
|
return ret;
|
||||||
|
case 0x14: // ISTAT
|
||||||
|
//DebugLog("53C810: ISTAT read\n");
|
||||||
|
return Ctx.regISTAT;
|
||||||
|
case 0x1C: // TEMP 7-0
|
||||||
|
return Ctx.regTEMP&0xFF;
|
||||||
|
case 0x1D: // TEMP 15-8
|
||||||
|
return (Ctx.regTEMP>>8)&0xFF;
|
||||||
|
case 0x1E: // TEMP 23-16
|
||||||
|
return (Ctx.regTEMP>>16)&0xFF;
|
||||||
|
case 0x1F: // TEMP 31-24
|
||||||
|
return (Ctx.regTEMP>>24)&0xFF;
|
||||||
|
case 0x24: // DBC 7-0
|
||||||
|
return Ctx.regDBC&0xFF;
|
||||||
|
case 0x25: // DBC 15-8
|
||||||
|
return (Ctx.regDBC>>8)&0xFF;
|
||||||
|
case 0x26: // DBC 23-16
|
||||||
|
return (Ctx.regDBC>>16)&0xFF;
|
||||||
|
case 0x27: // DCMD
|
||||||
|
return Ctx.regDCMD;
|
||||||
|
case 0x2C: // DSP 7-0
|
||||||
|
return Ctx.regDSP&0xFF;
|
||||||
|
case 0x2D: // DSP 15-8
|
||||||
|
return (Ctx.regDSP>>8)&0xFF;
|
||||||
|
case 0x2E: // DSP 23-16
|
||||||
|
return (Ctx.regDSP>>16)&0xFF;
|
||||||
|
case 0x2F: // DSP 31-24
|
||||||
|
return (Ctx.regDSP>>24)&0xFF;
|
||||||
|
case 0x30: // DSPS 7-0
|
||||||
|
return Ctx.regDSPS&0xFF;
|
||||||
|
case 0x31: // DSPS 15-8
|
||||||
|
return (Ctx.regDSPS>>8)&0xFF;
|
||||||
|
case 0x32: // DSPS 23-16
|
||||||
|
return (Ctx.regDSPS>>16)&0xFF;
|
||||||
|
case 0x33: // DSPS 31-24
|
||||||
|
return (Ctx.regDSPS>>24)&0xFF;
|
||||||
|
case 0x38:
|
||||||
|
return Ctx.regDMODE;
|
||||||
|
case 0x3B: // DCNTL
|
||||||
|
return Ctx.regDCNTL;
|
||||||
|
default: // get it from the register file
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register file should be up to date
|
||||||
|
return Ctx.regs[reg&0xFF];
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT32 C53C810::ReadPCIConfigSpace(unsigned device, unsigned reg, unsigned bits, unsigned offset)
|
||||||
|
{
|
||||||
|
UINT32 d;
|
||||||
|
|
||||||
|
if ((bits==8))
|
||||||
|
{
|
||||||
|
DebugLog("53C810 %d-bit PCI read request for reg=%02X\n", bits, reg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (reg)
|
||||||
|
{
|
||||||
|
case 0x00: // Device ID and Vendor ID
|
||||||
|
d = 0x00011000; // 0x1000 = LSI Logic
|
||||||
|
switch (bits)
|
||||||
|
{
|
||||||
|
case 8:
|
||||||
|
d >>= (3-offset)*8; // offset will be 0-3; select appropriate byte
|
||||||
|
d &= 0xFF;
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
d >>= (2-offset)*8; // offset will be 0 or 2 only; select either high or low word
|
||||||
|
d &= 0xFFFF;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return d;
|
||||||
|
default:
|
||||||
|
DebugLog("53C810 PCI read request for reg=%02X (%d-bit)\n", reg, bits);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void C53C810::WritePCIConfigSpace(unsigned device, unsigned reg, unsigned bits, unsigned offset, UINT32 data)
|
||||||
|
{
|
||||||
|
DebugLog("53C810 PCI %d-bit write request for reg=%02X, data=%08X\n", bits, reg, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void C53C810::Reset(void)
|
||||||
|
{
|
||||||
|
memset(Ctx.regs, 0, sizeof(Ctx.regs));
|
||||||
|
Ctx.regs[0x00] = 0xC0; // SCNTL0
|
||||||
|
Ctx.regs[0x0C] = 0x80; // DSTAT
|
||||||
|
Ctx.regs[0x0F] = 0x02; // SSTAT2
|
||||||
|
Ctx.regs[0x18] = 0xFF; // reserved
|
||||||
|
Ctx.regs[0x19] = 0xF0; // CTEST1
|
||||||
|
Ctx.regs[0x1A] = 0x01; // CTEST2
|
||||||
|
Ctx.regs[0x46] = 0x60; // MACNTL
|
||||||
|
Ctx.regs[0x47] = 0x0F; // GPCNTL
|
||||||
|
Ctx.regs[0x4C] = 0x03; // STEST0
|
||||||
|
Ctx.regTEMP = 0;
|
||||||
|
Ctx.regDSP = 0;
|
||||||
|
Ctx.regDSPS = 0;
|
||||||
|
Ctx.regDBC = 0;
|
||||||
|
Ctx.regDCMD = 0;
|
||||||
|
Ctx.regDCNTL = 0;
|
||||||
|
Ctx.regDMODE = 0;
|
||||||
|
Ctx.regDSTAT = 0x80; // DMA FIFO empty
|
||||||
|
Ctx.regISTAT = 0;
|
||||||
|
Ctx.halt = FALSE;
|
||||||
|
|
||||||
|
DebugLog("53C810 reset\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Configuration, Initialization, and Shutdown
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void C53C810::Init(CBus *BusObjectPtr, CIRQ *IRQObjectPtr, unsigned scsiIRQBit)
|
||||||
|
{
|
||||||
|
Ctx.Bus = BusObjectPtr;
|
||||||
|
Ctx.IRQ = IRQObjectPtr;
|
||||||
|
Ctx.scsiIRQ = scsiIRQBit;
|
||||||
|
}
|
||||||
|
|
||||||
|
C53C810::C53C810(void)
|
||||||
|
{
|
||||||
|
BuildOpTable();
|
||||||
|
Ctx.Bus = NULL;
|
||||||
|
Ctx.IRQ = NULL;
|
||||||
|
scsiIRQ = 0;
|
||||||
|
DebugLog("Built 53C810\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
C53C810::~C53C810(void)
|
||||||
|
{
|
||||||
|
Ctx.Bus = NULL;
|
||||||
|
Ctx.IRQ = NULL;
|
||||||
|
DebugLog("Destroyed 53C810\n");
|
||||||
|
}
|
202
Model3/53C810.h
Normal file
202
Model3/53C810.h
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 53C810.h
|
||||||
|
*
|
||||||
|
* Header file defining the C53C810 class (NCR 53C810 SCSI controller).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDED_53C810_H
|
||||||
|
#define INCLUDED_53C810_H
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* struct NCR53C810Context:
|
||||||
|
*
|
||||||
|
* Context information for an NCR 53C810 device. Used internally by the C53C810
|
||||||
|
* class (defined below).
|
||||||
|
*/
|
||||||
|
struct NCR53C810Context
|
||||||
|
{
|
||||||
|
UINT8 regs[0x60];
|
||||||
|
|
||||||
|
// Registers defined below should not be read from here, not regs[]
|
||||||
|
UINT32 regTEMP; // TEMP
|
||||||
|
UINT32 regDSP; // DSP: DMA SCRIPTS Pointer
|
||||||
|
UINT32 regDSPS; // DSPS: DMA SCRIPTS Pointer Save
|
||||||
|
UINT32 regDBC; // DBC: DMA Byte Counter (24 bits)
|
||||||
|
UINT8 regDCMD; // DCMD: DMA Command
|
||||||
|
UINT8 regDCNTL; // DCNTL: DMA Control
|
||||||
|
UINT8 regDMODE; // DMODE: DMA Mode
|
||||||
|
UINT8 regDSTAT; // DSTAT: DMA Status (read only)
|
||||||
|
UINT8 regISTAT; // ISTAT: Interrupt Status
|
||||||
|
|
||||||
|
// Operational status
|
||||||
|
BOOL halt; // set TRUE if halted by interrupt instruction
|
||||||
|
|
||||||
|
// Big endian bus object for DMA memory access and instruction fetching
|
||||||
|
CBus *Bus;
|
||||||
|
|
||||||
|
// IRQ handling
|
||||||
|
CIRQ *IRQ; // IRQ controller
|
||||||
|
unsigned scsiIRQ; // IRQ bit to use when calling IRQ handler
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* C53C810:
|
||||||
|
*
|
||||||
|
* NCR 53C810 SCSI controller device.
|
||||||
|
*/
|
||||||
|
class C53C810: public CPCIDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
* SaveState(SaveState):
|
||||||
|
*
|
||||||
|
* Saves an image of the current device state.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* SaveState Block file to save state information to.
|
||||||
|
*/
|
||||||
|
void SaveState(CBlockFile *SaveState);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LoadState(SaveState):
|
||||||
|
*
|
||||||
|
* Loads and a state image.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* SaveState Block file to load state information from.
|
||||||
|
*/
|
||||||
|
void LoadState(CBlockFile *SaveState);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ReadRegister(reg):
|
||||||
|
*
|
||||||
|
* Read from an operating register (8 bits at a time).
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* reg Register offset ranging from 0x00 to 0x5F. Anything higher
|
||||||
|
* is ignored and returns 0.
|
||||||
|
*/
|
||||||
|
UINT8 ReadRegister(unsigned reg);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WriteRegister(reg, data):
|
||||||
|
*
|
||||||
|
* Write to an operating register (8 bits at a time). When breaking multi-
|
||||||
|
* byte words into individual bytes, make sure to write the lowest address
|
||||||
|
* first and the highest last. Some special functions registers, like DSP,
|
||||||
|
* will initiate processing only when the highest byte is written.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* reg Register offset ranging from 0x00 to 0x5F. Anything higher
|
||||||
|
* is ignored.
|
||||||
|
* data Data to write.
|
||||||
|
*/
|
||||||
|
void WriteRegister(unsigned reg, UINT8 data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ReadPCIConfigSpace(device, reg, bits, offset):
|
||||||
|
*
|
||||||
|
* Reads a PCI configuration space register. See CPCIDevice definition for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* device Device number (ignored, not needed).
|
||||||
|
* reg Register number.
|
||||||
|
* bits Bit width of access (8, 16, or 32 only).;
|
||||||
|
* offset Byte offset within register, aligned to the specified bit
|
||||||
|
* width, and offset from the 32-bit aligned base of the
|
||||||
|
* register number.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Register data.
|
||||||
|
*/
|
||||||
|
UINT32 ReadPCIConfigSpace(unsigned device, unsigned reg, unsigned bits, unsigned width);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WritePCIConfigSpace(device, reg, bits, offset, data):
|
||||||
|
*
|
||||||
|
* Writes to a PCI configuration space register. See CPCIDevice definition
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* device Device number (ignored, not needed).
|
||||||
|
* reg Register number.
|
||||||
|
* bits Bit width of access (8, 16, or 32 only).
|
||||||
|
* offset Byte offset within register, aligned to the specified bit
|
||||||
|
* width, and offset from the 32-bit aligned base of the
|
||||||
|
* register number.
|
||||||
|
* data Data.
|
||||||
|
*/
|
||||||
|
void WritePCIConfigSpace(unsigned device, unsigned reg, unsigned bits, unsigned width, UINT32 data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset(void):
|
||||||
|
*
|
||||||
|
* Resets the device.
|
||||||
|
*/
|
||||||
|
void Reset(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Init(BusObjectPtr, IRQObjectPtr, scsiIRQBit):
|
||||||
|
*
|
||||||
|
* One-time initialization of the context. Must be called prior to all
|
||||||
|
* other members.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* BusObjectPtr Pointer to the bus that the 53C810 has control
|
||||||
|
* over. Used to read/write memory.
|
||||||
|
* IRQObjectPtr Pointer to the IRQ controller. Used to trigger SCSI
|
||||||
|
* and DMA interrupts.
|
||||||
|
* scsiIRQBit IRQ identifier bit to pass along to IRQ controller
|
||||||
|
* when asserting interrupts.
|
||||||
|
*/
|
||||||
|
void Init(CBus *BusObjectPtr, CIRQ *IRQObjectPtr, unsigned scsiIRQBit);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* C53C810(void):
|
||||||
|
* ~C53C810(void):
|
||||||
|
*
|
||||||
|
* Constructor and destructor.
|
||||||
|
*/
|
||||||
|
C53C810(void);
|
||||||
|
~C53C810(void);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Private members
|
||||||
|
void Run(int numOps);
|
||||||
|
void BuildOpTable(void);
|
||||||
|
void Insert(UINT8 mask, UINT8 op, BOOL (*Handler)(struct NCR53C810Context *));
|
||||||
|
BOOL (*OpTable[256])(struct NCR53C810Context *);
|
||||||
|
|
||||||
|
// Context (register file)
|
||||||
|
struct NCR53C810Context Ctx;
|
||||||
|
|
||||||
|
// IRQ controller and IRQ identifier for this SCSI controller
|
||||||
|
CIRQ *IRQ;
|
||||||
|
unsigned scsiIRQ;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // INCLUDED_53C810_H
|
281
Model3/53C810Disasm.cpp
Normal file
281
Model3/53C810Disasm.cpp
Normal file
|
@ -0,0 +1,281 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 53C810Disasm.cpp
|
||||||
|
*
|
||||||
|
* NCR 53C810 SCRIPTS disassembler.
|
||||||
|
*
|
||||||
|
* WARNING: Not all instructions are supported. They are being added as they
|
||||||
|
* are encountered in Model 3 games. Some of the complicated operand forms may
|
||||||
|
* be decoded incorrectly.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#ifdef STANDALONE
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
|
#include "Types.h"
|
||||||
|
|
||||||
|
#define DISASM_VERSION "1.0"
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Disassembler Function
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DisassembleSCRIPTS(op, addr, mnem):
|
||||||
|
*
|
||||||
|
* Disassembles one SCRIPTS instruction.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* op Three consecutive words.
|
||||||
|
* addr Current instruction address.
|
||||||
|
* mnem Buffer to write instruction mnemonic to. If no instruction was
|
||||||
|
* successfully decoded, will be set to '\0'.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Number of 32-bit words decoded (1, 2, or 3) or 0 if the instruction was
|
||||||
|
* not recognized.
|
||||||
|
*/
|
||||||
|
int DisassembleSCRIPTS(UINT32 op[3], UINT32 addr, char *mnem)
|
||||||
|
{
|
||||||
|
static const char *phase[] = { "data_out", "data_in", "command", "status", "res4", "res5", "message_out", "message_in" };
|
||||||
|
int carry, bitTrue, compData, compPhase, wait, mask, data;
|
||||||
|
|
||||||
|
mnem[0] = '\0';
|
||||||
|
|
||||||
|
if ((op[0]&0xC0000000) == 0)
|
||||||
|
{
|
||||||
|
// Block Move
|
||||||
|
if ((op[0]&0x10000000)) // FROM
|
||||||
|
sprintf(mnem, "move (%d) from %08X, when ", (op[0]>>27)&1, op[1]);
|
||||||
|
else // count
|
||||||
|
{
|
||||||
|
if ((op[0]&0x20000000))
|
||||||
|
sprintf(mnem, "move (%d) %X, ptr %08X, when ", (op[0]>>27)&1, op[0]&0x00FFFFFF, op[1]);
|
||||||
|
else
|
||||||
|
sprintf(mnem, "move (%d) %X, %08X, when ", (op[0]>>27)&1, op[0]&0x00FFFFFF, op[1]);
|
||||||
|
}
|
||||||
|
strcat(mnem, phase[(op[0]>>24)&7]);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
else if ((op[0]&0xE0000000) == 0xC0000000)
|
||||||
|
{
|
||||||
|
// Move Memory
|
||||||
|
if ((op[0]&0x01000000))
|
||||||
|
sprintf(mnem, "move memory no flush %X, %08X, %08X", op[0]&0x00FFFFFF, op[1], op[2]);
|
||||||
|
else
|
||||||
|
sprintf(mnem, "move memory %X, %08X, %08X", op[0]&0x00FFFFFF, op[1], op[2]);
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
else if ((op[0]&0xF8000000) == 0x98000000)
|
||||||
|
{
|
||||||
|
// INT and INTFLY
|
||||||
|
mnem += sprintf(mnem, "%s %08X, ", (op[0]&0x00100000)?"intfly ":"int ", op[1]);
|
||||||
|
|
||||||
|
carry = op[0]&0x00200000;
|
||||||
|
bitTrue = op[0]&0x00080000;
|
||||||
|
compData = op[0]&0x00040000; // data (if set)
|
||||||
|
compPhase = op[0]&0x00020000; // phase (if set) (or atn if neither phase nor data set)
|
||||||
|
wait = op[0]&0x00010000;
|
||||||
|
data = op[0]&0xFF;
|
||||||
|
mask = (op[0]>>8)&0xFF;
|
||||||
|
|
||||||
|
if (carry)
|
||||||
|
sprintf(mnem, "%s%s carry", wait?"if":"when", bitTrue?"":" not");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mnem += sprintf(mnem, "%s%s ", wait?"if":"when", bitTrue?"":" not");
|
||||||
|
if (!compPhase && !compData)
|
||||||
|
sprintf(mnem, "atn");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (compPhase)
|
||||||
|
{
|
||||||
|
mnem += sprintf(mnem, phase[(op[0]>>24)&7]);
|
||||||
|
if (compData)
|
||||||
|
mnem += sprintf(mnem, " and ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compData)
|
||||||
|
mnem += sprintf(mnem, "%02X mask %02X", data, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
return 0; // no match found
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Standalone Disassembler
|
||||||
|
|
||||||
|
Define STANDALONE to build a command line-driven SCRIPTS disassembler.
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifdef STANDALONE
|
||||||
|
|
||||||
|
static void PrintUsage(void)
|
||||||
|
{
|
||||||
|
puts("scriptsd Version "DISASM_VERSION" by Bart Trzynadlowski: NCR 53C810 SCRIPTS Disassembler");
|
||||||
|
puts("Usage: scriptsd <file> [options]");
|
||||||
|
puts("Options: -?,-h Show this help text");
|
||||||
|
puts(" -s <offset> Start offset (hexadecimal)");
|
||||||
|
puts(" -l <num> Number of instructions (Default=16)");
|
||||||
|
puts(" -o <addr> Set origin (hexadecimal)");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* main(argc, argv):
|
||||||
|
*
|
||||||
|
* Standalone SCRIPTS disassembler.
|
||||||
|
*/
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
char mnem[64];
|
||||||
|
FILE *fp;
|
||||||
|
UINT8 *buffer;
|
||||||
|
unsigned i, num, offset, fsize, start = 0, len, org, file = 0;
|
||||||
|
UINT32 op[3];
|
||||||
|
BOOL len_specified = 0, org_specified = 0;
|
||||||
|
char *c;
|
||||||
|
|
||||||
|
|
||||||
|
if (argc <= 1)
|
||||||
|
PrintUsage();
|
||||||
|
|
||||||
|
for (i = 1; i < argc; i++)
|
||||||
|
{
|
||||||
|
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-?"))
|
||||||
|
PrintUsage();
|
||||||
|
else if (!strcmp(argv[i], "-s"))
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
if (i >= argc)
|
||||||
|
fprintf(stderr, "scriptsd: warning: no argument to %s\n", "-s");
|
||||||
|
else
|
||||||
|
start = strtoul(argv[i], &c, 16);
|
||||||
|
}
|
||||||
|
else if (!strcmp(argv[i], "-l"))
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
if (i >= argc)
|
||||||
|
fprintf(stderr, "scriptsd: warning: no argument to %s\n", "-l");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
len = atoi(argv[i]);
|
||||||
|
len_specified = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!strcmp(argv[i], "-o"))
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
if (i >= argc)
|
||||||
|
fprintf(stderr, "scriptsd: warning: no argument to %s\n", "-o");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
org = strtoul(argv[i], &c, 16);
|
||||||
|
org_specified = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
file = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "scriptsd: no input file specified\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Load file
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ((fp = fopen(argv[file], "rb")) == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "scriptsd: failed to open file: %s\n", argv[file]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
fseek(fp, 0, SEEK_END);
|
||||||
|
fsize = ftell(fp);
|
||||||
|
rewind(fp);
|
||||||
|
|
||||||
|
// Allocate some extra padding for operands
|
||||||
|
if ((buffer = (UINT8 *) calloc(fsize+4*3, sizeof(UINT8))) == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "scriptsd: not enough memory to load input file: %s, %lu bytes\n", argv[file], (unsigned long) fsize);
|
||||||
|
fclose(fp);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
fread(buffer, sizeof(UINT8), fsize, fp);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
if (!len_specified)
|
||||||
|
len = 16;
|
||||||
|
|
||||||
|
if (!org_specified)
|
||||||
|
org = start;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Disassemble!
|
||||||
|
*/
|
||||||
|
|
||||||
|
offset = start;
|
||||||
|
for (i = 0; (i<len) && (offset<fsize); i++)
|
||||||
|
{
|
||||||
|
op[0] = (buffer[offset+3]<<24)|(buffer[offset+2]<<16)|(buffer[offset+1]<<8)|buffer[offset+0];
|
||||||
|
op[1] = (buffer[offset+7]<<24)|(buffer[offset+6]<<16)|(buffer[offset+5]<<8)|buffer[offset+4];
|
||||||
|
op[2] = (buffer[offset+11]<<24)|(buffer[offset+10]<<16)|(buffer[offset+9]<<8)|buffer[offset+8];
|
||||||
|
|
||||||
|
num = DisassembleSCRIPTS(op, org, mnem);
|
||||||
|
if (0 == num)
|
||||||
|
{
|
||||||
|
printf("%08X: %08X ?\n", org, op[0]);
|
||||||
|
offset += 4;
|
||||||
|
org += 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (num == 3)
|
||||||
|
printf("%08X: %08X %08X %08X %s\n", org, op[0], op[1], op[2], mnem);
|
||||||
|
else if (num == 2)
|
||||||
|
printf("%08X: %08X %08X %s\n", org, op[0], op[1], mnem);
|
||||||
|
else
|
||||||
|
printf("%08X: %08X %s\n", org, op[0], mnem);
|
||||||
|
offset += num*4;
|
||||||
|
org += num*4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(buffer);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
263
Model3/93C46.cpp
Normal file
263
Model3/93C46.cpp
Normal file
|
@ -0,0 +1,263 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 93C46.cpp
|
||||||
|
*
|
||||||
|
* Implementation of the 93C46 serial EEPROM device.
|
||||||
|
*
|
||||||
|
* To-Do List
|
||||||
|
* ----------
|
||||||
|
* - Manual says that when a READ command is issued, the data will be
|
||||||
|
* preceded by a leading 0. This seems to cause problems. Perhaps DO
|
||||||
|
* should just be set to 0?
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "Supermodel.h"
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Save States
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void C93C46::SaveState(CBlockFile *SaveState)
|
||||||
|
{
|
||||||
|
SaveState->NewBlock("93C46", __FILE__);
|
||||||
|
SaveState->Write(regs, sizeof(regs));
|
||||||
|
SaveState->Write(&CS, sizeof(CS));
|
||||||
|
SaveState->Write(&CLK, sizeof(CLK));
|
||||||
|
SaveState->Write(&DI, sizeof(DI));
|
||||||
|
SaveState->Write(&DO, sizeof(DO));
|
||||||
|
SaveState->Write(&bitBufferOut, sizeof(bitBufferOut));
|
||||||
|
SaveState->Write(&bitBufferIn, sizeof(bitBufferIn));
|
||||||
|
SaveState->Write(&bitsOut, sizeof(bitsOut));
|
||||||
|
SaveState->Write(&receiving, sizeof(receiving));
|
||||||
|
SaveState->Write(&addr, sizeof(addr));
|
||||||
|
SaveState->Write(&busyCycles, sizeof(busyCycles));
|
||||||
|
SaveState->Write(&locked, sizeof(locked));
|
||||||
|
}
|
||||||
|
|
||||||
|
void C93C46::LoadState(CBlockFile *SaveState)
|
||||||
|
{
|
||||||
|
if (OKAY != SaveState->FindBlock("93C46"))
|
||||||
|
{
|
||||||
|
ErrorLog("Unable to load EEPROM state. File is corrupted.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveState->Read(regs, sizeof(regs));
|
||||||
|
SaveState->Read(&CS, sizeof(CS));
|
||||||
|
SaveState->Read(&CLK, sizeof(CLK));
|
||||||
|
SaveState->Read(&DI, sizeof(DI));
|
||||||
|
SaveState->Read(&DO, sizeof(DO));
|
||||||
|
SaveState->Read(&bitBufferOut, sizeof(bitBufferOut));
|
||||||
|
SaveState->Read(&bitBufferIn, sizeof(bitBufferIn));
|
||||||
|
SaveState->Read(&bitsOut, sizeof(bitsOut));
|
||||||
|
SaveState->Read(&receiving, sizeof(receiving));
|
||||||
|
SaveState->Read(&addr, sizeof(addr));
|
||||||
|
SaveState->Read(&busyCycles, sizeof(busyCycles));
|
||||||
|
SaveState->Read(&locked, sizeof(locked));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Emulation Functions
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
// Reverse the bit ordering
|
||||||
|
static UINT16 ReverseBits16(UINT16 data)
|
||||||
|
{
|
||||||
|
UINT16 dataOut = 0, hi, lo;
|
||||||
|
|
||||||
|
for (int i = 0; i < 16/2; i++)
|
||||||
|
{
|
||||||
|
// Isolate corresponding bits from high and low halves of word
|
||||||
|
lo = (data>>i)&1;
|
||||||
|
hi = (data>>(15-i))&1;
|
||||||
|
|
||||||
|
// Swap them
|
||||||
|
dataOut |= (lo<<(15-i));
|
||||||
|
dataOut |= (hi<<i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
void C93C46::Write(unsigned pinCS, unsigned pinCLK, unsigned pinDI)
|
||||||
|
{
|
||||||
|
unsigned prevCLK;
|
||||||
|
|
||||||
|
//printf("EEPROM: CS=%d CLK=%d DI=%d\n", pinCS, pinCLK, pinDI);
|
||||||
|
prevCLK = CLK;
|
||||||
|
|
||||||
|
// Save current inputs
|
||||||
|
CS = !!pinCS;
|
||||||
|
CLK = !!pinCLK;
|
||||||
|
DI = !!pinDI;
|
||||||
|
|
||||||
|
// Active high CS. When it's brought low, reset control logic.
|
||||||
|
if (CS == 0)
|
||||||
|
{
|
||||||
|
bitBufferIn = 0; // this must be cleared each time (only leading 0's can exist prior to commands)
|
||||||
|
receiving = TRUE; // ready to accept commands
|
||||||
|
busyCycles = 5; // some applications require the chip to take time while writing
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rising clock edge
|
||||||
|
if (!prevCLK && CLK)
|
||||||
|
{
|
||||||
|
if (receiving == TRUE) // is the chip receiving commands?
|
||||||
|
{
|
||||||
|
// Shift in a new bit
|
||||||
|
bitBufferIn <<= 1;
|
||||||
|
bitBufferIn |= DI;
|
||||||
|
|
||||||
|
// Detect commands
|
||||||
|
if ((bitBufferIn&0xFFFFFFC0) == 0x180) // READ
|
||||||
|
{
|
||||||
|
addr = bitBufferIn&0x3F;
|
||||||
|
bitBufferOut = ReverseBits16(regs[addr]); // reverse so that D15 is shifted out first
|
||||||
|
//bitBufferOut <<= 1; // a leading 0 precedes the first word read (causes problems)
|
||||||
|
bitsOut = 0; // how many bits read out
|
||||||
|
receiving = FALSE; // transmitting data now
|
||||||
|
DebugLog("93C46: READ %X\n", addr);
|
||||||
|
}
|
||||||
|
else if (bitBufferIn == 0x13) // WEN (write enable)
|
||||||
|
{
|
||||||
|
locked = FALSE;
|
||||||
|
DebugLog("93C46: WEN\n");
|
||||||
|
}
|
||||||
|
else if (bitBufferIn == 0x10) // WDS (write disable)
|
||||||
|
{
|
||||||
|
locked = TRUE;
|
||||||
|
DebugLog("93C46: WDS\n");
|
||||||
|
}
|
||||||
|
else if ((bitBufferIn&0xFFC00000) == 0x01400000) // WRITE
|
||||||
|
{
|
||||||
|
if (!locked)
|
||||||
|
regs[(bitBufferIn>>16)&0x3F] = bitBufferIn&0xFFFF;
|
||||||
|
DO = 1; // ready (write completed)
|
||||||
|
DebugLog("93C46: WRITE %X=%04X (lock=%d)\n", (bitBufferIn>>16)&0x3F, bitBufferIn&0xFFFF, locked);
|
||||||
|
}
|
||||||
|
else if ((bitBufferIn&0xFFF00000) == 0x01100000) // WRALL (write all)
|
||||||
|
{
|
||||||
|
if (!locked)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 64; i++)
|
||||||
|
regs[i] = bitBufferIn&0xFFFF;
|
||||||
|
}
|
||||||
|
DO = 1;
|
||||||
|
DebugLog("93C46: WRALL %04X (lock=%d)\n", bitBufferIn&0xFFFF, locked);
|
||||||
|
}
|
||||||
|
else if ((bitBufferIn&0xFFFFFFC0) == 0x1C0) // ERASE
|
||||||
|
{
|
||||||
|
if (!locked)
|
||||||
|
regs[bitBufferIn&0x3F] = 0xFFFF;
|
||||||
|
DO = 1;
|
||||||
|
DebugLog("93C46: ERASE %X (lock=%d)\n", bitBufferIn&0x3F, locked);
|
||||||
|
}
|
||||||
|
else if ((bitBufferIn&0xFFFFFFF0) == 0x120) // ERALL (erase all)
|
||||||
|
{
|
||||||
|
if (!locked)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 64; i++)
|
||||||
|
regs[i] = 0xFFFF;
|
||||||
|
DebugLog("93C46: ERALL (lock=%d)\n", locked);
|
||||||
|
}
|
||||||
|
DO = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // the chip is reading out data (transmitting)
|
||||||
|
{
|
||||||
|
// Shift out to DO pin
|
||||||
|
DO = bitBufferOut&1;
|
||||||
|
bitBufferOut >>= 1;
|
||||||
|
++bitsOut;
|
||||||
|
|
||||||
|
// If we've shifted out an entire 16-bit word, load up the next address (no preceding 0)
|
||||||
|
if (bitsOut == 16)
|
||||||
|
{
|
||||||
|
addr = (addr+1)&0x3F;
|
||||||
|
bitBufferOut = ReverseBits16(regs[addr]);
|
||||||
|
bitsOut = 0;
|
||||||
|
|
||||||
|
DebugLog("93C46: Next word loaded: %X\n", addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned C93C46::Read(void)
|
||||||
|
{
|
||||||
|
// When not transmitting, DO indicates whether busy or not
|
||||||
|
if (receiving)
|
||||||
|
{
|
||||||
|
if (busyCycles > 0) // simulate programming delay
|
||||||
|
{
|
||||||
|
--busyCycles;
|
||||||
|
return 0; // busy
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return 1; // ready to accept new command
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transmit data
|
||||||
|
return DO;
|
||||||
|
}
|
||||||
|
|
||||||
|
void C93C46::Clear(void)
|
||||||
|
{
|
||||||
|
memset(regs, 0xFF, sizeof(regs));
|
||||||
|
}
|
||||||
|
|
||||||
|
void C93C46::Reset(void)
|
||||||
|
{
|
||||||
|
receiving = TRUE;
|
||||||
|
locked = TRUE;
|
||||||
|
bitBufferIn = 0;
|
||||||
|
bitBufferOut = 0;
|
||||||
|
addr = 0;
|
||||||
|
busyCycles = 0;
|
||||||
|
CS = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Configuration, Initialization, and Shutdown
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void C93C46::Init(void)
|
||||||
|
{
|
||||||
|
// this function really only exists for consistency with other device classes
|
||||||
|
}
|
||||||
|
|
||||||
|
C93C46::C93C46(void)
|
||||||
|
{
|
||||||
|
memset(regs, 0xFF, sizeof(regs));
|
||||||
|
DebugLog("Built 93C46 EEPROM\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
C93C46::~C93C46(void)
|
||||||
|
{
|
||||||
|
DebugLog("Destroyed 93C46 EEPROM\n");
|
||||||
|
}
|
127
Model3/93C46.h
Normal file
127
Model3/93C46.h
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 93C46.h
|
||||||
|
*
|
||||||
|
* Header file defining the C93C46 class: 93C46 EEPROM.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDED_93C46_H
|
||||||
|
#define INCLUDED_93C46_H
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* C93C46:
|
||||||
|
*
|
||||||
|
* 93C46 serial EEPROM.
|
||||||
|
*/
|
||||||
|
class C93C46
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
* SaveState(SaveState):
|
||||||
|
*
|
||||||
|
* Saves an image of the current device state.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* SaveState Block file to save state information to.
|
||||||
|
*/
|
||||||
|
void SaveState(CBlockFile *SaveState);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LoadState(SaveState):
|
||||||
|
*
|
||||||
|
* Loads and a state image.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* SaveState Block file to load state information from.
|
||||||
|
*/
|
||||||
|
void LoadState(CBlockFile *SaveState);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clear(void):
|
||||||
|
*
|
||||||
|
* Clears the EEPROM contents by writing all 1's.
|
||||||
|
*/
|
||||||
|
void Clear(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write(pinCS, pinCLK, pinDI):
|
||||||
|
*
|
||||||
|
* Write to the EEPROM. All inputs must be either 0 or 1 only!
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* pinCS Chip select.
|
||||||
|
* pinCLK Serial data clock.
|
||||||
|
* pinDI Serial data input.
|
||||||
|
*/
|
||||||
|
void Write(unsigned pinCS, unsigned pinCLK, unsigned pinDI);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read(void):
|
||||||
|
*
|
||||||
|
* Read from the EEPROM.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* The serial data output bit (either a 1 or 0).
|
||||||
|
*/
|
||||||
|
unsigned Read(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset(void):
|
||||||
|
*
|
||||||
|
* Resets the device, putting it into a locked state. Does not modify the
|
||||||
|
* memory.
|
||||||
|
*/
|
||||||
|
void Reset(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Init(void):
|
||||||
|
*
|
||||||
|
* One-time initialization of the context. Must be called prior to all
|
||||||
|
* other members.
|
||||||
|
*/
|
||||||
|
void Init(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* C93C46(void):
|
||||||
|
* ~C93C46(void):
|
||||||
|
*
|
||||||
|
* Constructor and destructor.
|
||||||
|
*/
|
||||||
|
C93C46(void);
|
||||||
|
~C93C46(void);
|
||||||
|
|
||||||
|
private:
|
||||||
|
UINT16 regs[64]; // memory: 64 16-bit registers
|
||||||
|
unsigned CS, CLK, DI, DO; // pins
|
||||||
|
UINT32 bitBufferOut; // bits to be shifted out
|
||||||
|
UINT32 bitBufferIn; // stores bits as they are shifted in
|
||||||
|
int bitsOut; // how many bits have been shifted out
|
||||||
|
BOOL receiving; // if true, accepting data, if false, sending data out (read commands)
|
||||||
|
unsigned addr; // latched address
|
||||||
|
int busyCycles; // when > 0, counts down delay cycles and indicates busy
|
||||||
|
BOOL locked; // whether the EEPROM is in a locked state
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // INCLUDED_93C46_H
|
81
Model3/Bus.h
Normal file
81
Model3/Bus.h
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bus.h
|
||||||
|
*
|
||||||
|
* Header file for the CBus abstract base class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDED_BUS_H
|
||||||
|
#define INCLUDED_BUS_H
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CBus:
|
||||||
|
*
|
||||||
|
* An abstract base class for an address bus. Defines handlers for 8-, 16-,
|
||||||
|
* 32-, and 64-bit random access. All accesses are big endian.
|
||||||
|
*/
|
||||||
|
class CBus
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
* Read8(addr):
|
||||||
|
* Read16(addr):
|
||||||
|
* Read32(addr):
|
||||||
|
* Read64(addr):
|
||||||
|
*
|
||||||
|
* Read handlers.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* addr Address (caller should ensure it is aligned to the correct
|
||||||
|
* boundary, corresponding to the size).
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Data of the appropriate size (8, 16, 32, or 64 bits).
|
||||||
|
*/
|
||||||
|
virtual UINT8 Read8(UINT32 addr) = 0;
|
||||||
|
virtual UINT16 Read16(UINT32 addr) = 0;
|
||||||
|
virtual UINT32 Read32(UINT32 addr) = 0;
|
||||||
|
virtual UINT64 Read64(UINT32 addr) = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write8(addr, data):
|
||||||
|
* Write16(addr, data):
|
||||||
|
* Write32(addr, data):
|
||||||
|
* Write64(addr, data):
|
||||||
|
*
|
||||||
|
* Write handlers.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* addr Address (caller should ensure it is aligned to the correct
|
||||||
|
* boundary, corresponding to the size).
|
||||||
|
* data Data to write.
|
||||||
|
*/
|
||||||
|
virtual void Write8(UINT32 addr, UINT8 data) = 0;
|
||||||
|
virtual void Write16(UINT32 addr, UINT16 data) = 0;
|
||||||
|
virtual void Write32(UINT32 addr, UINT32 data) = 0;
|
||||||
|
virtual void Write64(UINT32 addr, UINT64 data) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // INCLUDED_BUS_H
|
126
Model3/IRQ.cpp
Normal file
126
Model3/IRQ.cpp
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IRQ.cpp
|
||||||
|
*
|
||||||
|
* Model 3 IRQ controller. Implementation of the CIRQ class.
|
||||||
|
*
|
||||||
|
* To-Do List:
|
||||||
|
* -----------
|
||||||
|
* - When a proper OO CPU core is added, the CPU object should be hooked to the
|
||||||
|
* IRQ controller to assert/deassert the PowerPC IRQ line. Right now, we just
|
||||||
|
* call the PPC core directly, which should not happen in proper OO code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Supermodel.h"
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Save States
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void CIRQ::SaveState(CBlockFile *SaveState)
|
||||||
|
{
|
||||||
|
SaveState->NewBlock("IRQ", __FILE__);
|
||||||
|
SaveState->Write(&irqEnable, sizeof(irqEnable));
|
||||||
|
SaveState->Write(&irqState, sizeof(irqState));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CIRQ::LoadState(CBlockFile *SaveState)
|
||||||
|
{
|
||||||
|
if (OKAY != SaveState->FindBlock("IRQ"))
|
||||||
|
{
|
||||||
|
ErrorLog("Unable to load IRQ controller state. Save state file is corrupted.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveState->Read(&irqEnable, sizeof(irqEnable));
|
||||||
|
SaveState->Read(&irqState, sizeof(irqState));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Emulation Functions
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void CIRQ::Assert(unsigned irqBits)
|
||||||
|
{
|
||||||
|
irqState |= irqBits;
|
||||||
|
if ((irqState&irqEnable)) // low 8 bits are maskable interrupts
|
||||||
|
//ppc_set_irq_line(0);
|
||||||
|
ppc_set_irq_line(1);
|
||||||
|
if ((irqState&(~0xFF))) // any non-maskable interrupts pending?
|
||||||
|
//ppc_set_irq_line(0);
|
||||||
|
ppc_set_irq_line(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TO-DO: CPU needs to have deassert logic!
|
||||||
|
void CIRQ::Deassert(unsigned irqBits)
|
||||||
|
{
|
||||||
|
irqState &= ~irqBits;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CIRQ::WriteIRQEnable(UINT8 data)
|
||||||
|
{
|
||||||
|
irqEnable = (unsigned) data;
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT8 CIRQ::ReadIRQEnable(void)
|
||||||
|
{
|
||||||
|
return (UINT8) (irqEnable&0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT8 CIRQ::ReadIRQState(void)
|
||||||
|
{
|
||||||
|
return (UINT8) (irqState&0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CIRQ::Reset(void)
|
||||||
|
{
|
||||||
|
irqEnable = 0; // disable all
|
||||||
|
irqState = 0; // no IRQs pending
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Configuration, Initialization, and Shutdown
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void CIRQ::Init(void)
|
||||||
|
{
|
||||||
|
// this function really only exists for consistency with other device classes
|
||||||
|
}
|
||||||
|
|
||||||
|
CIRQ::CIRQ(void)
|
||||||
|
{
|
||||||
|
DebugLog("Built IRQ controller\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CIRQ::~CIRQ(void):
|
||||||
|
*
|
||||||
|
* Destructor.
|
||||||
|
*/
|
||||||
|
CIRQ::~CIRQ(void)
|
||||||
|
{
|
||||||
|
DebugLog("Destroyed IRQ controller\n");
|
||||||
|
}
|
148
Model3/IRQ.h
Normal file
148
Model3/IRQ.h
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IRQ.h
|
||||||
|
*
|
||||||
|
* Header file defining the CIRQ class: Model 3 IRQ controller.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDED_IRQ_H
|
||||||
|
#define INCLUDED_IRQ_H
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CIRQ:
|
||||||
|
*
|
||||||
|
* Model 3 IRQ controller.
|
||||||
|
*/
|
||||||
|
class CIRQ
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
* SaveState(SaveState):
|
||||||
|
*
|
||||||
|
* Saves an image of the current device state.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* SaveState Block file to save state information to.
|
||||||
|
*/
|
||||||
|
void SaveState(CBlockFile *SaveState);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LoadState(SaveState):
|
||||||
|
*
|
||||||
|
* Loads and a state image.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* SaveState Block file to load state information from.
|
||||||
|
*/
|
||||||
|
void LoadState(CBlockFile *SaveState);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Assert(irqBits):
|
||||||
|
*
|
||||||
|
* Assert IRQ(s). Also asserts the CPU IRQ line (unless no IRQs are
|
||||||
|
* actually asserted). The IRQ controller only tracks 8 state bits but for
|
||||||
|
* non-maskable interrupts, higher bits (bits 9 and above) can be used.
|
||||||
|
* They will be tracked internally but not visible when external hardware
|
||||||
|
* tries reading the IRQ state.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* irqBits IRQ bits corresponding to the IRQ state register (1
|
||||||
|
* indicates assertion, 0 does nothing).
|
||||||
|
*/
|
||||||
|
void Assert(unsigned irqBits);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Deassert(irqBits):
|
||||||
|
*
|
||||||
|
* Deasserts the specified IRQs and, if there are no pending IRQs left,
|
||||||
|
* deasserts the CPU IRQ line.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* irqBits IRQ bits corresponding to the IRQ state register (1
|
||||||
|
* indicates deassertion, 0 does nothing).
|
||||||
|
*/
|
||||||
|
void Deassert(unsigned irqBits);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ReadIRQEnable(void):
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* The 8-bit IRQ enable register.
|
||||||
|
*/
|
||||||
|
UINT8 ReadIRQEnable(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WriteIRQEnable(data):
|
||||||
|
*
|
||||||
|
* Write to the IRQ enable register. A 1 indicates the IRQ is enabled. Only
|
||||||
|
* the first 8 IRQs can be masked.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* data IRQ enable bits (8 bits).
|
||||||
|
*/
|
||||||
|
void WriteIRQEnable(UINT8 data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ReadIRQState(void):
|
||||||
|
*
|
||||||
|
* Accesses the IRQ state register. Set bits indicate pending IRQs. Only
|
||||||
|
* the low 8 bits are returned. Any IRQs occupying bits above this are
|
||||||
|
* effectively invisible and non-maskable.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* The 8-bit IRQ state register.
|
||||||
|
*/
|
||||||
|
UINT8 ReadIRQState(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset(void):
|
||||||
|
*
|
||||||
|
* Resets the IRQ controller. All IRQs are disabled.
|
||||||
|
*/
|
||||||
|
void Reset(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Init(void):
|
||||||
|
*
|
||||||
|
* One-time initialization of the context. Must be called prior to all
|
||||||
|
* other members.
|
||||||
|
*/
|
||||||
|
void Init(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CIRQ(void):
|
||||||
|
* ~CIRQ(void):
|
||||||
|
*
|
||||||
|
* Constructor and destructor.
|
||||||
|
*/
|
||||||
|
CIRQ(void);
|
||||||
|
~CIRQ(void);
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned irqEnable; // 8 bits, 1=enabled, 0=disabled
|
||||||
|
unsigned irqState; // bits correspond to irqEnable, 1=pending, 0=not pending
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // INCLUDED_IRQ_H
|
357
Model3/MPC10x.cpp
Normal file
357
Model3/MPC10x.cpp
Normal file
|
@ -0,0 +1,357 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MPC10x.cpp
|
||||||
|
*
|
||||||
|
* Implementation of the CMPC10x class: Motorola MPC105 and MPC106 PCI/bridge
|
||||||
|
* controllers. For now, only a single PCI bus can be attached and all requests
|
||||||
|
* are forwarded to it.
|
||||||
|
*
|
||||||
|
* This is a minimal implementation designed with the Model 3 in mind. It does
|
||||||
|
* not properly emulate the device and there are numerous inaccuracies in this
|
||||||
|
* code.
|
||||||
|
*
|
||||||
|
* Important Problems To Be Aware Of
|
||||||
|
* ---------------------------------
|
||||||
|
*
|
||||||
|
* The handling of accesses smaller than 32 bits is funky and probably very
|
||||||
|
* incorrect, particularly for non-32-bit-aligned register numbers. This
|
||||||
|
* problem extends to the entire PCI emulation.
|
||||||
|
*
|
||||||
|
* Little endian mode is not supported (the internal registers, however, seem
|
||||||
|
* to be stored in little endian format). The registers are implemented as byte
|
||||||
|
* read/writeable but this is not accurate. In fact, some are read only, and
|
||||||
|
* some can only be accessed as 16-bit or 32-bit words.
|
||||||
|
*
|
||||||
|
* This code assumes we are running on a little endian machine and that the
|
||||||
|
* PowerPC is a big endian machine. Therefore, bytes can be written to the
|
||||||
|
* register space directly but multi-byte words must be flipped before being
|
||||||
|
* interpreted internally.
|
||||||
|
*
|
||||||
|
* External configuration registers are not implemented.
|
||||||
|
*
|
||||||
|
* Multiple PCI buses are not yet supported (everything is assumed to be on
|
||||||
|
* bus 0).
|
||||||
|
*
|
||||||
|
* ... And lots more!
|
||||||
|
*
|
||||||
|
* References
|
||||||
|
* ----------
|
||||||
|
* 1. "MPC106 PCI/Bridge/Memory Controller User's Manual" (MPC106UM/D Rev.1)
|
||||||
|
*
|
||||||
|
* To-Do List
|
||||||
|
* ----------
|
||||||
|
* - The whole PCI system desperately needs a re-write to deal with different
|
||||||
|
* bit-width accesses in a cleaner manner.
|
||||||
|
* - Fix endian confusion. Should assume everything written is in the correct
|
||||||
|
* endian, pushing the responsibility onto the caller.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "Supermodel.h"
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Save States
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void CMPC10x::SaveState(CBlockFile *SaveState)
|
||||||
|
{
|
||||||
|
SaveState->NewBlock("MPC10x", __FILE__);
|
||||||
|
SaveState->Write(regs, sizeof(regs));
|
||||||
|
SaveState->Write(&pciBus, sizeof(pciBus));
|
||||||
|
SaveState->Write(&pciDevice, sizeof(pciDevice));
|
||||||
|
SaveState->Write(&pciFunction, sizeof(pciFunction));
|
||||||
|
SaveState->Write(&pciReg, sizeof(pciReg));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMPC10x::LoadState(CBlockFile *SaveState)
|
||||||
|
{
|
||||||
|
if (OKAY != SaveState->FindBlock("MPC10x"))
|
||||||
|
{
|
||||||
|
ErrorLog("Unable to load MPC%X state. Save state file is corrupted.", model);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveState->Read(regs, sizeof(regs));
|
||||||
|
SaveState->Read(&pciBus, sizeof(pciBus));
|
||||||
|
SaveState->Read(&pciDevice, sizeof(pciDevice));
|
||||||
|
SaveState->Read(&pciFunction, sizeof(pciFunction));
|
||||||
|
SaveState->Read(&pciReg, sizeof(pciReg));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Emulation Functions
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CMPC10x::WritePCIConfigAddress(data):
|
||||||
|
*
|
||||||
|
* Writes to the PCI configuration space address (CONFIG_ADDR) register, which
|
||||||
|
* selects a PCI device and configuration space register.
|
||||||
|
*/
|
||||||
|
void CMPC10x::WritePCIConfigAddress(UINT32 data)
|
||||||
|
{
|
||||||
|
UINT32 d = FLIPENDIAN32(data);
|
||||||
|
|
||||||
|
pciBus = (d>>16)&0xFF;
|
||||||
|
pciDevice = (d>>11)&0x1F;
|
||||||
|
pciFunction = (d>>8)&7;
|
||||||
|
pciReg = d&0xFF; // NOTE: for actual PCI devices (device>0), register must be shifted right by 2 and clamped to 0x3F
|
||||||
|
|
||||||
|
// The manual is unclear as to whether device 0 (MPC10x) register #s are clamped to 0x3F or not. Pay attention to this!
|
||||||
|
#ifdef DEBUG
|
||||||
|
if (pciDevice == 0)
|
||||||
|
{
|
||||||
|
DebugLog("MPC10x: Device 0 configuration access: %08X\n", d);
|
||||||
|
if ((d&3))
|
||||||
|
ErrorLog("MPC10x: Device 0 configuration address with low bits set: %08X\n", d);
|
||||||
|
}
|
||||||
|
//DebugLog("MPC10x: Bus=%X, Device=%X, Function=%X, Reg=%X\n", pciBus, pciDevice, pciFunction, pciReg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (pciBus != 0)
|
||||||
|
{
|
||||||
|
//printf("Multiple PCI buses detected!\n");
|
||||||
|
DebugLog("Multiple PCI buses detected!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CMPC10x::ReadPCIConfigData(bits, offset):
|
||||||
|
*
|
||||||
|
* Reads from the PCI configuration space data (CONFIG_DATA) register, which in
|
||||||
|
* turn calls upon the selected PCI device to return the data.
|
||||||
|
*/
|
||||||
|
UINT32 CMPC10x::ReadPCIConfigData(unsigned bits, unsigned offset)
|
||||||
|
{
|
||||||
|
// Handle self-access first
|
||||||
|
if (pciDevice == 0)
|
||||||
|
{
|
||||||
|
// Alignment check
|
||||||
|
#ifdef DEBUG
|
||||||
|
if (((bits==16)&&(offset&1)) || ((bits==32)&&(offset&3)))
|
||||||
|
ErrorLog("Misaligned MPC10x read request (bits=%d,reg=%X,offset=%d)\n", bits, pciReg, offset);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
switch (bits)
|
||||||
|
{
|
||||||
|
case 8:
|
||||||
|
return regs[pciReg+offset];
|
||||||
|
case 16:
|
||||||
|
return (regs[pciReg+offset+0]<<8) | regs[pciReg+offset+1];
|
||||||
|
case 32:
|
||||||
|
return (regs[pciReg+offset+0]<<24) |
|
||||||
|
(regs[pciReg+offset+1]<<16) |
|
||||||
|
(regs[pciReg+offset+2]<<8) |
|
||||||
|
regs[pciReg+offset+3];
|
||||||
|
default:
|
||||||
|
ErrorLog("MPC10x internal error: invalid access size (%d-bits)", bits);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// All other PCI devices passed to PCI bus
|
||||||
|
return PCIBus->ReadConfigSpace(pciDevice, (pciReg>>2)&0x3C, bits, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CMPC10x::WritePCIConfigData(bits, offset, data):
|
||||||
|
*
|
||||||
|
* Writes to the PCI configuration space data (CONFIG_DATA) register, which in
|
||||||
|
* turn passes the data to the selected PCI device.
|
||||||
|
*/
|
||||||
|
void CMPC10x::WritePCIConfigData(unsigned bits, unsigned offset, UINT32 data)
|
||||||
|
{
|
||||||
|
// Handle self-access first
|
||||||
|
if (pciDevice == 0)
|
||||||
|
{
|
||||||
|
// Alignment check
|
||||||
|
#ifdef DEBUG
|
||||||
|
if (((bits==16)&&(offset&1)) || ((bits==32)&&(offset&3)))
|
||||||
|
ErrorLog("Misaligned MPC10x read request (bits=%d,reg=%X,offset=%d)\n", bits, pciReg, offset);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
switch (bits)
|
||||||
|
{
|
||||||
|
case 8:
|
||||||
|
regs[pciReg+offset] = data&0xFF;
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
regs[pciReg+offset+0] = (data>>8)&0xFF;
|
||||||
|
regs[pciReg+offset+1] = data&0xFF;
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
|
regs[pciReg+offset+0] = (data>>24)&0xFF;
|
||||||
|
regs[pciReg+offset+1] = (data>>16)&0xFF;
|
||||||
|
regs[pciReg+offset+2] = (data>>8)&0xFF;
|
||||||
|
regs[pciReg+offset+3] = data&0xFF;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ErrorLog("MPC10x internal error: invalid access size (%d-bits)", bits);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PCIBus->WriteConfigSpace(pciDevice, (pciReg>>2)&0x3C, bits, offset, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CMPC10x::WriteRegister(reg, data):
|
||||||
|
*
|
||||||
|
* Writes to the MPC10x register space. Accesses one byte at a time so it
|
||||||
|
* should be endian-neutral (the caller ends up being responsible for this).
|
||||||
|
*/
|
||||||
|
void CMPC10x::WriteRegister(unsigned reg, UINT8 data)
|
||||||
|
{
|
||||||
|
regs[reg&0xFF] = data;
|
||||||
|
|
||||||
|
if ((reg&0xFF)==0xA8)
|
||||||
|
{
|
||||||
|
if ((data&0x20))
|
||||||
|
ErrorLog("MPC10x little endian mode not yet implemented!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CMPC10x::Reset(void):
|
||||||
|
*
|
||||||
|
* Resets the device.
|
||||||
|
*/
|
||||||
|
void CMPC10x::Reset(void)
|
||||||
|
{
|
||||||
|
memset(regs, 0, sizeof(regs));
|
||||||
|
|
||||||
|
// Data is actually stored in little endian format, so we can write directly here
|
||||||
|
*(UINT16 *) ®s[0x00] = 0x1057; // vendor ID (Motorola)
|
||||||
|
*(UINT16 *) ®s[0x02] = (model==0x106)?0x0002:0x0001; // device ID (MPC105 or MPC106)
|
||||||
|
|
||||||
|
if (model == 0x106) // MPC106
|
||||||
|
{
|
||||||
|
*(UINT32 *) ®s[0x04] = 0x00800006; // PCI command and PCI status
|
||||||
|
*(UINT32 *) ®s[0x08] = 0x00060000; // class code and revision ID
|
||||||
|
*(UINT32 *) ®s[0x0C] = 0x00000800; // cache line size
|
||||||
|
*(UINT32 *) ®s[0x70] = 0x00CD0000; // output driver control
|
||||||
|
*(UINT32 *) ®s[0xA8] = 0x0010FF00; // processor interface config. 1
|
||||||
|
*(UINT32 *) ®s[0xAC] = 0x060C000C; // processor interface config. 2
|
||||||
|
*(UINT32 *) ®s[0xB8] = 0x04000000; // TO-DO: CHECK MANUAL
|
||||||
|
*(UINT32 *) ®s[0xC0] = 0x00000100; // error enabling 1
|
||||||
|
*(UINT32 *) ®s[0xE0] = 0x00420FFF; // emulation support configuration 1
|
||||||
|
*(UINT32 *) ®s[0xE8] = 0x00200000; // emulation support configuration 2
|
||||||
|
*(UINT32 *) ®s[0xF0] = 0x0000FF02; // memory control config. 1
|
||||||
|
*(UINT32 *) ®s[0xF4] = 0x00030000; // memory control config. 2
|
||||||
|
*(UINT32 *) ®s[0xFC] = 0x00000010; // memory control config. 4
|
||||||
|
}
|
||||||
|
else // MPC105
|
||||||
|
{
|
||||||
|
*(UINT32 *) ®s[0x04] = 0x00800006; // PCI command and PCI status
|
||||||
|
*(UINT32 *) ®s[0x08] = 0x00060000; // class code and revision ID
|
||||||
|
*(UINT32 *) ®s[0xA8] = 0x0010FF00; // processor interface config. 1
|
||||||
|
*(UINT32 *) ®s[0xAC] = 0x060C000C; // processor interface config. 2
|
||||||
|
*(UINT32 *) ®s[0xB8] = 0x04000000; // TO-DO: CHECK MANUAL
|
||||||
|
*(UINT32 *) ®s[0xF0] = 0x0000FF02; // memory control config. 1
|
||||||
|
*(UINT32 *) ®s[0xF4] = 0x00030000; // memory control config. 2
|
||||||
|
*(UINT32 *) ®s[0xFC] = 0x00000010; // memory control config. 4
|
||||||
|
// To-do: any more??
|
||||||
|
}
|
||||||
|
|
||||||
|
pciBus = 0;
|
||||||
|
pciDevice = 0;
|
||||||
|
pciFunction = 0;
|
||||||
|
pciReg = 0;
|
||||||
|
|
||||||
|
DebugLog("MPC%X reset\n", model);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Configuration, Initialization, and Shutdown
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CMPC10x:AttachPCIBus(BusObjectPtr):
|
||||||
|
*
|
||||||
|
* Attaches a PCI bus object which will handle all PCI register requests.
|
||||||
|
*/
|
||||||
|
void CMPC10x::AttachPCIBus(CPCIBus *BusObjectPtr)
|
||||||
|
{
|
||||||
|
PCIBus = BusObjectPtr;
|
||||||
|
DebugLog("MPC10x connected to a PCI bus\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CMPC10x::SetModel(modelNum):
|
||||||
|
*
|
||||||
|
* Sets the device behavior to either MPC105 or MPC106.
|
||||||
|
*/
|
||||||
|
void CMPC10x::SetModel(int modelNum)
|
||||||
|
{
|
||||||
|
model = modelNum;
|
||||||
|
|
||||||
|
if ((modelNum!=0x105) && (modelNum!=0x106))
|
||||||
|
{
|
||||||
|
ErrorLog("%s:%s: Invalid MPC10x model number (%X).", __FILE__, __LINE__, modelNum);
|
||||||
|
model = 0x105;
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugLog("MPC10x set to MPC%X\n", model);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CMPC10x::Init():
|
||||||
|
*
|
||||||
|
* This must be called first and only once during the lifetime of the class.
|
||||||
|
*/
|
||||||
|
void CMPC10x::Init(void)
|
||||||
|
{
|
||||||
|
// this function really only exists for consistency with other device classes
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CMPC10x::CMPC10x(void):
|
||||||
|
*
|
||||||
|
* Constructor.
|
||||||
|
*/
|
||||||
|
CMPC10x::CMPC10x(void)
|
||||||
|
{
|
||||||
|
PCIBus = NULL;
|
||||||
|
model = 0x105; // default to MPC105
|
||||||
|
pciBus = 0;
|
||||||
|
pciDevice = 0;
|
||||||
|
pciFunction = 0;
|
||||||
|
pciReg = 0;
|
||||||
|
DebugLog("Built MPC10x\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CMPC10x::~CMPC10x(void):
|
||||||
|
*
|
||||||
|
* Destructor.
|
||||||
|
*/
|
||||||
|
CMPC10x::~CMPC10x(void)
|
||||||
|
{
|
||||||
|
PCIBus = NULL;
|
||||||
|
DebugLog("Destroyed MPC10x\n");
|
||||||
|
}
|
178
Model3/MPC10x.h
Normal file
178
Model3/MPC10x.h
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MPC10x.h
|
||||||
|
*
|
||||||
|
* Header file defining the CMPC10x class (Motorola MPC105 and MPC106).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDED_MPC10X_H
|
||||||
|
#define INCLUDED_MPC10X_H
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CMPC10x:
|
||||||
|
*
|
||||||
|
* MPC105 or MPC106 PCI/bridge/memory controller device.
|
||||||
|
*/
|
||||||
|
class CMPC10x
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
* SaveState(SaveState):
|
||||||
|
*
|
||||||
|
* Saves an image of the current device state.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* SaveState Block file to save state information to.
|
||||||
|
*/
|
||||||
|
void SaveState(CBlockFile *SaveState);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LoadState(SaveState):
|
||||||
|
*
|
||||||
|
* Loads and a state image.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* SaveState Block file to load state information from.
|
||||||
|
*/
|
||||||
|
void LoadState(CBlockFile *SaveState);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WritePCIConfigAddress(data):
|
||||||
|
*
|
||||||
|
* Writes to the PCI configuration space address (CONFIG_ADDR) register,
|
||||||
|
* which selects a PCI device and register. For now, only 32-bit accesses
|
||||||
|
* are supported.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* data Data to write.
|
||||||
|
*/
|
||||||
|
void WritePCIConfigAddress(UINT32 data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ReadPCIConfigData(bits, offset):
|
||||||
|
*
|
||||||
|
* Reads from the PCI configuration space data (CONFIG_DATA) register,
|
||||||
|
* which in turn calls upon the selected PCI device to return the data.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* bits Width of access (8, 16, or 32 only).
|
||||||
|
* offset Byte offset within CONFIG_DATA, aligned to bit width of
|
||||||
|
* access. For bytes, can be 0, 1, 2, or 3, but for 16-bit
|
||||||
|
* words can only be 0 or 2. The offset is just the
|
||||||
|
* CONFIG_DATA address ANDed with 0, 2, or 3 depending on the
|
||||||
|
* bit width (32, 16, or 8, respectively).
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Data from the PCI conifguration space of the presently selected
|
||||||
|
* device. Bit width will be the same as specified by 'bits'.
|
||||||
|
*/
|
||||||
|
UINT32 ReadPCIConfigData(unsigned bits, unsigned offset);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WritePCIConfigData(bits, offset, data):
|
||||||
|
*
|
||||||
|
* Writes data (CONFIG_DATA) to the presently selected PCI device and
|
||||||
|
* register.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* bits Width of access (8, 16, or 32 only).
|
||||||
|
* offset Byte offset within CONFIG_DATA, aligned to bit width of
|
||||||
|
* access.
|
||||||
|
* data Data to write.
|
||||||
|
*/
|
||||||
|
void WritePCIConfigData(unsigned bits, unsigned offset, UINT32 data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WriteRegister(reg, data):
|
||||||
|
*
|
||||||
|
* Writes to the MPC10x's internal registers.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* reg Register (0-255).
|
||||||
|
* data Data to write.
|
||||||
|
*/
|
||||||
|
void WriteRegister(unsigned reg, UINT8 data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset(void):
|
||||||
|
*
|
||||||
|
* Resets the device.
|
||||||
|
*/
|
||||||
|
void Reset(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AttachPCIBus(BusObjectPtr):
|
||||||
|
*
|
||||||
|
* Attaches a PCI bus object which will handle all PCI register accesses.
|
||||||
|
* For now, only one bus is supported. The MPCI10x will automatically
|
||||||
|
* handle any requests for device 0 (itself) without passing them on to the
|
||||||
|
* PCI bus.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* BusObjectPtr A pointer to the PCI bus object.
|
||||||
|
*/
|
||||||
|
void AttachPCIBus(CPCIBus *BusObjectPtr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SetModel(modelNum):
|
||||||
|
*
|
||||||
|
* Selects either MPC105 or MPC106 behavior. This will also reset the
|
||||||
|
* device state by calling Reset(). This should be called prior to any
|
||||||
|
* other emulation functions and after Init().
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* modelNum Either 0x105 for MPC105 or 0x106 for MPC106. Defaults
|
||||||
|
* to MPC105 if unrecognized.
|
||||||
|
*/
|
||||||
|
void SetModel(int modelNum);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Init(void):
|
||||||
|
*
|
||||||
|
* One-time initialization of the context. Must be called prior to all
|
||||||
|
* other members. By default, initializes to MPC105.
|
||||||
|
*/
|
||||||
|
void Init(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CMPC10x(void):
|
||||||
|
* ~CMPC10x(void):
|
||||||
|
*
|
||||||
|
* Constructor and destructor for MPC10x class.
|
||||||
|
*/
|
||||||
|
CMPC10x(void);
|
||||||
|
~CMPC10x(void);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int model; // MPC105 = 0x105 (default), MPC106 = 0x106
|
||||||
|
CPCIBus *PCIBus; // the PCI bus to which this MPC10x is attached
|
||||||
|
UINT8 regs[256]; // internal registers
|
||||||
|
unsigned pciBus; // PCI bus component of PCI address setting (8 bits)
|
||||||
|
unsigned pciDevice; // PCI device component (5 bits)
|
||||||
|
unsigned pciFunction; // PCI function component (3 bits)
|
||||||
|
unsigned pciReg; // PCI register component (7 bits)
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // INCLUDED_MPC10X_H
|
2363
Model3/Model3.cpp
Normal file
2363
Model3/Model3.cpp
Normal file
File diff suppressed because it is too large
Load diff
318
Model3/Model3.h
Normal file
318
Model3/Model3.h
Normal file
|
@ -0,0 +1,318 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Model3.h
|
||||||
|
*
|
||||||
|
* Header file defining the CModel3 and CModel3Inputs classes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDED_MODEL3_H
|
||||||
|
#define INCLUDED_MODEL3_H
|
||||||
|
|
||||||
|
#include "Inputs/Inputs.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CModel3:
|
||||||
|
*
|
||||||
|
* A complete Model 3 system.
|
||||||
|
*
|
||||||
|
* Inherits CBus in order to pass the address space handlers to devices that
|
||||||
|
* may need them (CPU, DMA, etc.)
|
||||||
|
*
|
||||||
|
* NOTE: Currently NOT re-entrant due to a non-OOP PowerPC core. Do NOT create
|
||||||
|
* create more than one CModel3 object!
|
||||||
|
*/
|
||||||
|
class CModel3: public CBus, public CPCIDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
* ReadPCIConfigSpace(device, reg, bits, offset):
|
||||||
|
*
|
||||||
|
* Handles unknown PCI devices. See CPCIDevice definition for more details.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* device Device number.
|
||||||
|
* reg Register number.
|
||||||
|
* bits Bit width of access (8, 16, or 32 only).;
|
||||||
|
* offset Byte offset within register, aligned to the specified bit
|
||||||
|
* width, and offset from the 32-bit aligned base of the
|
||||||
|
* register number.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Register data.
|
||||||
|
*/
|
||||||
|
UINT32 ReadPCIConfigSpace(unsigned device, unsigned reg, unsigned bits, unsigned width);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WritePCIConfigSpace(device, reg, bits, offset, data):
|
||||||
|
*
|
||||||
|
* Handles unknown PCI devices. See CPCIDevice definition for more details.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* device Device number.
|
||||||
|
* reg Register number.
|
||||||
|
* bits Bit width of access (8, 16, or 32 only).
|
||||||
|
* offset Byte offset within register, aligned to the specified bit
|
||||||
|
* width, and offset from the 32-bit aligned base of the
|
||||||
|
* register number.
|
||||||
|
* data Data.
|
||||||
|
*/
|
||||||
|
void WritePCIConfigSpace(unsigned device, unsigned reg, unsigned bits, unsigned width, UINT32 data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read8(addr):
|
||||||
|
* Read16(addr):
|
||||||
|
* Read32(addr):
|
||||||
|
* Read64(addr):
|
||||||
|
*
|
||||||
|
* Read a byte, 16-bit half word, 32-bit word, or 64-bit double word from
|
||||||
|
* the PowerPC address space. This implements the PowerPC address bus. Note
|
||||||
|
* that it is big endian, so when accessing from a little endian device,
|
||||||
|
* the byte order must be manually reversed.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* addr Address to read.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Data at the address.
|
||||||
|
*/
|
||||||
|
UINT8 Read8(UINT32 addr);
|
||||||
|
UINT16 Read16(UINT32 addr);
|
||||||
|
UINT32 Read32(UINT32 addr);
|
||||||
|
UINT64 Read64(UINT32 addr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write8(addr, data):
|
||||||
|
* Write16(addr, data):
|
||||||
|
* Write32(addr, data):
|
||||||
|
* Write64(addr, data):
|
||||||
|
*
|
||||||
|
* Write a byte, half word, word, or double word to the PowerPC address
|
||||||
|
* space. Note that everything is stored in big endian form, so when
|
||||||
|
* accessing with a little endian device, the byte order must be manually
|
||||||
|
* reversed.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* addr Address to write.
|
||||||
|
* data Data to write.
|
||||||
|
*/
|
||||||
|
void Write8(UINT32 addr, UINT8 data);
|
||||||
|
void Write16(UINT32 addr, UINT16 data);
|
||||||
|
void Write32(UINT32 addr, UINT32 data);
|
||||||
|
void Write64(UINT32 addr, UINT64 data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SaveState(SaveState):
|
||||||
|
*
|
||||||
|
* Saves an image of the current state.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* SaveState Block file to save state information to.
|
||||||
|
*/
|
||||||
|
void SaveState(CBlockFile *SaveState);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LoadState(SaveState):
|
||||||
|
*
|
||||||
|
* Loads and resumes execution from a state image.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* SaveState Block file to load state information from.
|
||||||
|
*/
|
||||||
|
void LoadState(CBlockFile *SaveState);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SaveNVRAM(NVRAM):
|
||||||
|
*
|
||||||
|
* Saves an image of the current NVRAM state.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* NVRAM Block file to save NVRAM to.
|
||||||
|
*/
|
||||||
|
void SaveNVRAM(CBlockFile *NVRAM);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LoadNVRAM(NVRAM):
|
||||||
|
*
|
||||||
|
* Loads an NVRAM image.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* NVRAM Block file to load NVRAM state from.
|
||||||
|
*/
|
||||||
|
void LoadNVRAM(CBlockFile *NVRAM);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ClearNVRAM(void):
|
||||||
|
*
|
||||||
|
* Clears all NVRAM (backup RAM and EEPROM).
|
||||||
|
*/
|
||||||
|
void ClearNVRAM(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RunFrame(void):
|
||||||
|
*
|
||||||
|
* Runs one frame (assuming 60 Hz video refresh rate).
|
||||||
|
*/
|
||||||
|
void RunFrame(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset(void):
|
||||||
|
*
|
||||||
|
* Resets the system. Does not modify non-volatile memory.
|
||||||
|
*/
|
||||||
|
void Reset(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GetGameInfo(void):
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* A pointer to the presently loaded game's information structure (or
|
||||||
|
* NULL if no ROM set has yet been loaded).
|
||||||
|
*/
|
||||||
|
const struct GameInfo * GetGameInfo(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LoadROMSet(GameList, zipFile):
|
||||||
|
*
|
||||||
|
* Loads a complete ROM set from the specified ZIP archive.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* GameList List of all supported games and their ROMs.
|
||||||
|
* zipFile ZIP file to load from.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* OKAY if successful, FAIL otherwise. Prints errors.
|
||||||
|
*/
|
||||||
|
BOOL LoadROMSet(const struct GameInfo *GameList, const char *zipFile);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AttachRenderers(Render2DPtr, Render3DPtr):
|
||||||
|
*
|
||||||
|
* Attaches the renderers to the appropriate device objects.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* Render2DPtr Pointer to a tile renderer object.
|
||||||
|
* Render3DPtr Same as above but for a 3D renderer.
|
||||||
|
*/
|
||||||
|
void AttachRenderers(CRender2D *Render2DPtr, CRender3D *Render3DPtr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AttachInputs(InputsPtr):
|
||||||
|
*
|
||||||
|
* Attaches OSD-managed inputs.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* InputsPtr Pointer to the object containing input states.
|
||||||
|
*/
|
||||||
|
void AttachInputs(CInputs *InputsPtr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Init(ppcFrequencyParam):
|
||||||
|
*
|
||||||
|
* One-time initialization of the context. Must be called prior to all
|
||||||
|
* other members. Allocates memory and initializes device states.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* ppcFrequencyParam PowerPC frequency in Hz. If less than 1 MHz,
|
||||||
|
* will be clamped to 1 MHz.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* OKAY is successful, otherwise FAILED if a non-recoverable error
|
||||||
|
* occurred. Prints own error messages.
|
||||||
|
*/
|
||||||
|
BOOL Init(unsigned ppcFrequencyParam);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CModel3(void):
|
||||||
|
* ~CModel3(void):
|
||||||
|
*
|
||||||
|
* Constructor and destructor for Model 3 class. Constructor performs a
|
||||||
|
* bare-bones initialization of object; does not perform any memory
|
||||||
|
* allocation or any actions that can fail. The destructor will deallocate
|
||||||
|
* memory and free resources used by the object (and its child objects).
|
||||||
|
*/
|
||||||
|
CModel3(void);
|
||||||
|
~CModel3(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Private Property.
|
||||||
|
* Tresspassers will be shot! ;)
|
||||||
|
*/
|
||||||
|
private:
|
||||||
|
|
||||||
|
// Private member functions
|
||||||
|
UINT8 ReadInputs(unsigned reg);
|
||||||
|
void WriteInputs(unsigned reg, UINT8 data);
|
||||||
|
UINT32 ReadSecurity(unsigned reg);
|
||||||
|
void WriteSecurity(unsigned reg, UINT32 data);
|
||||||
|
void SetCROMBank(unsigned idx);
|
||||||
|
UINT8 ReadSystemRegister(unsigned reg);
|
||||||
|
void WriteSystemRegister(unsigned reg, UINT8 data);
|
||||||
|
void Patch(void);
|
||||||
|
|
||||||
|
// Game and hardware information
|
||||||
|
const struct GameInfo *Game;
|
||||||
|
|
||||||
|
// Game inputs
|
||||||
|
CInputs *Inputs;
|
||||||
|
|
||||||
|
// Input registers (game controls)
|
||||||
|
UINT8 inputBank;
|
||||||
|
UINT8 serialFIFO1, serialFIFO2;
|
||||||
|
UINT8 gunReg;
|
||||||
|
int adcChannel;
|
||||||
|
|
||||||
|
// Emulated core Model 3 memory regions
|
||||||
|
UINT8 *memoryPool; // single allocated region for all ROM and system RAM
|
||||||
|
UINT8 *ram; // 8 MB PowerPC RAM
|
||||||
|
UINT8 *crom; // 8+128 MB CROM (fixed CROM first, then 64MB of banked CROMs -- Daytona2 might need extra?)
|
||||||
|
UINT8 *vrom; // 64 MB VROM (video ROM, visible only to Real3D)
|
||||||
|
UINT8 *soundROM; // 512 KB sound ROM (68K program)
|
||||||
|
UINT8 *sampleROM; // 8 MB samples (68K)
|
||||||
|
UINT8 *backupRAM; // 128 KB Backup RAM (battery backed)
|
||||||
|
UINT8 *securityRAM; // 128 KB Security Board RAM
|
||||||
|
|
||||||
|
// Banked CROM
|
||||||
|
UINT8 *cromBank; // currently mapped in CROM bank
|
||||||
|
unsigned cromBankReg; // the CROM bank register
|
||||||
|
|
||||||
|
// Security device
|
||||||
|
unsigned securityPtr; // pointer to current offset in security data
|
||||||
|
|
||||||
|
// PowerPC
|
||||||
|
PPC_FETCH_REGION PPCFetchRegions[3];
|
||||||
|
unsigned ppcFrequency; // clock frequency (Hz)
|
||||||
|
|
||||||
|
// Other devices
|
||||||
|
CIRQ IRQ; // Model 3 IRQ controller
|
||||||
|
CMPC10x PCIBridge; // MPC10x PCI/bridge/memory controller
|
||||||
|
CPCIBus PCIBus; // Model 3's PCI bus
|
||||||
|
C53C810 SCSI; // NCR 53C810 SCSI controller
|
||||||
|
CRTC72421 RTC; // Epson RTC-72421 real-time clock
|
||||||
|
C93C46 EEPROM; // 93C46 EEPROM
|
||||||
|
CTileGen TileGen; // Sega 2D tile generator
|
||||||
|
CReal3D GPU; // Real3D graphics hardware
|
||||||
|
CSoundBoard SoundBoard; // sound board
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // INCLUDED_MODEL3_H
|
154
Model3/PCI.cpp
Normal file
154
Model3/PCI.cpp
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PCI.cpp
|
||||||
|
*
|
||||||
|
* PCI bus and device emulation. Implementation of the CPCIBus class.
|
||||||
|
* CPCIDevice is just a base class from which others are derived. Only the
|
||||||
|
* transfer of PCI register data is emulated. Device functionality is
|
||||||
|
* handled by device objects (derived from CPCIDevice).
|
||||||
|
*
|
||||||
|
* TO-DO List:
|
||||||
|
* -----------
|
||||||
|
* - Exception handling for out-of-memory errors in STL? Right now, we assume
|
||||||
|
* the vectors do not run out of memory because so few PCI devices exist.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Supermodel.h"
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Emulation Functions
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CPCIBus::ReadConfigSpace(device, reg, bits offset):
|
||||||
|
*
|
||||||
|
* Reads a PCI register belonging to a particular device.
|
||||||
|
*/
|
||||||
|
UINT32 CPCIBus::ReadConfigSpace(unsigned device, unsigned reg, unsigned bits, unsigned offset)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
// Alignment check
|
||||||
|
#ifdef DEBUG
|
||||||
|
if (((bits==16)&&(offset&1)) || ((bits==32)&&(offset&3)))
|
||||||
|
ErrorLog("Misaligned PCI read request (device=%d,reg=%X,offset=%d)\n", device, reg, offset);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Search device vector for a matching device
|
||||||
|
for (i = 0; i < DeviceVector.size(); i++)
|
||||||
|
{
|
||||||
|
if (DeviceVector[i].device == device)
|
||||||
|
return DeviceVector[i].DeviceObject->ReadPCIConfigSpace(device, reg, bits, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugLog("PCI read request for unknown device (device=%d,reg=%X)\n", device, reg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CPCIBus::WriteConfigSpace(device, reg, bits, offset, data):
|
||||||
|
*
|
||||||
|
* Writes to the PCI configuration space register belonging to a particular
|
||||||
|
* device.
|
||||||
|
*/
|
||||||
|
void CPCIBus::WriteConfigSpace(unsigned device, unsigned reg, unsigned bits, unsigned offset, UINT32 data)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
// Search device vector for a matching device
|
||||||
|
for (i = 0; i < DeviceVector.size(); i++)
|
||||||
|
{
|
||||||
|
if (DeviceVector[i].device == device)
|
||||||
|
{
|
||||||
|
DeviceVector[i].DeviceObject->WritePCIConfigSpace(device, reg, bits, offset, data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// printf("PCI write request for unknown device (device=%d, reg=%X, data=%X)\n", device, reg, data);
|
||||||
|
DebugLog("PCI write request for unknown device (device=%d, reg=%X, data=%X)\n", device, reg, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CPCIBus::Reset():
|
||||||
|
*
|
||||||
|
* Resets the PCI bus emulation only. The devices attached to the bus are NOT
|
||||||
|
* reset or accessed.
|
||||||
|
*/
|
||||||
|
void CPCIBus::Reset(void)
|
||||||
|
{
|
||||||
|
// this function really only exists for consistency with other device classes
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Configuration, Initialization, and Shutdown
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CPCIBus::AttachDevice(device, DeviceObjectPtr):
|
||||||
|
*
|
||||||
|
* Attaches a device to the PCI bus. This means it is added to a list of
|
||||||
|
* devices to scan when handling PCI configuration space requests.
|
||||||
|
*/
|
||||||
|
void CPCIBus::AttachDevice(unsigned device, CPCIDevice *DeviceObjectPtr)
|
||||||
|
{
|
||||||
|
struct DeviceObjectLink D;
|
||||||
|
|
||||||
|
D.device = device;
|
||||||
|
D.DeviceObject = DeviceObjectPtr;
|
||||||
|
DeviceVector.push_back(D);
|
||||||
|
|
||||||
|
DebugLog("Attached device %d to PCI bus\n", device);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CPCIBus::Init():
|
||||||
|
*
|
||||||
|
* This must be called first and only once during the lifetime of the class.
|
||||||
|
*/
|
||||||
|
void CPCIBus::Init(void)
|
||||||
|
{
|
||||||
|
DeviceVector.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CPCIBus::CPCIBus(void):
|
||||||
|
*
|
||||||
|
* Constructor.
|
||||||
|
*/
|
||||||
|
CPCIBus::CPCIBus(void)
|
||||||
|
{
|
||||||
|
DebugLog("Built PCI bus\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CPCIBus::~CPCIBus(void):
|
||||||
|
*
|
||||||
|
* Destructor.
|
||||||
|
*/
|
||||||
|
CPCIBus::~CPCIBus(void)
|
||||||
|
{
|
||||||
|
DebugLog("Destroyed PCI bus\n");
|
||||||
|
}
|
190
Model3/PCI.h
Normal file
190
Model3/PCI.h
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PCI.h
|
||||||
|
*
|
||||||
|
* Header file for PCI bus and device emulation. Defines the CPCIDevice and
|
||||||
|
* CPCIBus classes.
|
||||||
|
*
|
||||||
|
* References
|
||||||
|
* ----------
|
||||||
|
* 1. "MPC106 PCI/Bridge/Memory Controller User's Manual" (MPC106UM/D Rev.1)
|
||||||
|
* Sec.7.4.5 describes the PCI configuration space header, which CPCIDevice-
|
||||||
|
* derived classes are supposed to implement.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDED_PCI_H
|
||||||
|
#define INCLUDED_PCI_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CPCIDevice:
|
||||||
|
*
|
||||||
|
* An abstract base class for PCI devices. Devices can inherit this class to
|
||||||
|
* provide PCI configuration space access handlers.
|
||||||
|
*/
|
||||||
|
class CPCIDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
* ReadPCIConfigSpace(reg, bits, offset):
|
||||||
|
*
|
||||||
|
* Reads a PCI configuration space register. Returns data in big endian
|
||||||
|
* form (little endian devices must byte reverse it).
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* device The device number. This is system-specific and ought to be
|
||||||
|
* ignored in most cases (devices ought to know what they
|
||||||
|
* are) but is provided in case multiple devices are handled
|
||||||
|
* by the same object.
|
||||||
|
* reg Register number (32-bit offset).
|
||||||
|
* bits Width of access (8, 16, or 32 only).
|
||||||
|
* offset Byte offset within register, aligned to bit width of
|
||||||
|
* access. For bytes, can be 0, 1, 2, or 3, but for 16-bit
|
||||||
|
* words can only be 0 or 2. Offsets are relative to the base
|
||||||
|
* of the 32-bit aligned register address, so register must be
|
||||||
|
* ANDed with ~3 first by caller.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Register data. Bit width will be the same as specified by 'bits'.
|
||||||
|
*/
|
||||||
|
virtual UINT32 ReadPCIConfigSpace(unsigned device, unsigned reg, unsigned bits, unsigned offset) = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WritePCIConfigSpace(reg, bits, offset, data):
|
||||||
|
*
|
||||||
|
* Writes to a PCI configuration space register.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* device Device number.
|
||||||
|
* reg Register number (32-bit offset).
|
||||||
|
* bits Width of access (8, 16, or 32 only).
|
||||||
|
* offset Byte offset within register, aligned to bit width of
|
||||||
|
* access.
|
||||||
|
* data Data. Interpreted according to specified bit width.
|
||||||
|
*/
|
||||||
|
virtual void WritePCIConfigSpace(unsigned device, unsigned reg, unsigned bits, unsigned offset, UINT32 data) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CPCIBus:
|
||||||
|
*
|
||||||
|
* A PCI bus. Dispatches commands to various attached devices. Because PCI
|
||||||
|
* configuration space access is not a critical or frequently-used function,
|
||||||
|
* dedicated 8-, 16-, and 32-bit handlers are not provided. Rather, the access
|
||||||
|
* size is passed to the handler to respond accordingly.
|
||||||
|
*/
|
||||||
|
class CPCIBus
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ReadConfigSpace(device, reg, bits, offset):
|
||||||
|
*
|
||||||
|
* Reads a PCI configuration space register belonging to a particular
|
||||||
|
* device.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* device PCI device ID.
|
||||||
|
* reg Register number.
|
||||||
|
* bits Width of access (8, 16, or 32 only).
|
||||||
|
* offset Byte offset within register, aligned to bit width of
|
||||||
|
* access. Offsets are relative to the base of the 32-bit
|
||||||
|
* aligned register address, so register must be ANDed with ~3
|
||||||
|
* first by caller.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Register data. Bit width will be the same as specified by 'bits'.
|
||||||
|
*/
|
||||||
|
UINT32 ReadConfigSpace(unsigned device, unsigned reg, unsigned bits, unsigned offset);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WriteConfigSpace(device, reg, bits, offset, data):
|
||||||
|
*
|
||||||
|
* Writes a PCI configuration space register.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* device PCI device ID.
|
||||||
|
* reg Register number.
|
||||||
|
* bits Width of access (8, 16, or 32 only).
|
||||||
|
* offset Byte offset within register, aligned to bit width of
|
||||||
|
* access.
|
||||||
|
* data Data being written. Interpreted according to bit width.
|
||||||
|
*/
|
||||||
|
void WriteConfigSpace(unsigned device, unsigned reg, unsigned bits, unsigned offset, UINT32 data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset(void):
|
||||||
|
*
|
||||||
|
* Resets the PCI bus emulation. Does not reset or access any devices
|
||||||
|
* attached to the PCI bus. These must be reset manually.
|
||||||
|
*/
|
||||||
|
void Reset(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AttachDevice(device, DeviceObjectPtr):
|
||||||
|
*
|
||||||
|
* Attaches a device to the PCI bus. This means it is added to a list of
|
||||||
|
* devices to scan when handling PCI configuration space requests.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* device PCI ID of the device being attached.
|
||||||
|
* DeviceObjectPtr Pointer to the device object.
|
||||||
|
*/
|
||||||
|
void AttachDevice(unsigned device, CPCIDevice *DeviceObjectPtr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Init(void):
|
||||||
|
*
|
||||||
|
* One-time initialization of the context. Must be called prior to all
|
||||||
|
* other members.
|
||||||
|
*/
|
||||||
|
void Init(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CPCIBus(void):
|
||||||
|
* ~CPCIBus(void):
|
||||||
|
*
|
||||||
|
* Constructor and destructor.
|
||||||
|
*/
|
||||||
|
CPCIBus(void);
|
||||||
|
~CPCIBus(void);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
// Map device IDs to objects
|
||||||
|
struct DeviceObjectLink
|
||||||
|
{
|
||||||
|
unsigned device;
|
||||||
|
CPCIDevice *DeviceObject;
|
||||||
|
};
|
||||||
|
|
||||||
|
// An array of device objects
|
||||||
|
vector<struct DeviceObjectLink> DeviceVector;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // INCLUDED_PCI_H
|
117
Model3/RTC72421.cpp
Normal file
117
Model3/RTC72421.cpp
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RTC72421.cpp
|
||||||
|
*
|
||||||
|
* Epson RTC-72421 implementation.
|
||||||
|
*
|
||||||
|
* TO-DO List:
|
||||||
|
* -----------
|
||||||
|
* - Rather than returning the host system time, should compute the "virtual"
|
||||||
|
* time inside the Model 3, which could differ.
|
||||||
|
* - Writes do nothing yet.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include "Supermodel.h"
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Emulation Functions
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
UINT8 CRTC72421::ReadRegister(unsigned reg)
|
||||||
|
{
|
||||||
|
time_t currentTime;
|
||||||
|
struct tm *Time;
|
||||||
|
|
||||||
|
time(¤tTime);
|
||||||
|
Time = localtime(¤tTime);
|
||||||
|
|
||||||
|
switch (reg&0xF)
|
||||||
|
{
|
||||||
|
case 0: // 1-second digit
|
||||||
|
return (Time->tm_sec % 10) & 0xF;
|
||||||
|
case 1: // 10-seconds digit
|
||||||
|
return (Time->tm_sec / 10) & 0x7;
|
||||||
|
case 2: // 1-minute digit
|
||||||
|
return (Time->tm_min % 10) & 0xF;
|
||||||
|
case 3: // 10-minute digit
|
||||||
|
return (Time->tm_min / 10) & 0x7;
|
||||||
|
case 4: // 1-hour digit
|
||||||
|
return (Time->tm_hour % 10) & 0xF;
|
||||||
|
case 5: // 10-hours digit
|
||||||
|
return (Time->tm_hour / 10) & 0x7;
|
||||||
|
case 6: // 1-day digit (days in month)
|
||||||
|
return (Time->tm_mday % 10) & 0xF;
|
||||||
|
case 7: // 10-days digit
|
||||||
|
return (Time->tm_mday / 10) & 0x3;
|
||||||
|
case 8: // 1-month digit
|
||||||
|
return ((Time->tm_mon + 1) % 10) & 0xF;
|
||||||
|
case 9: // 10-months digit
|
||||||
|
return ((Time->tm_mon + 1) / 10) & 0x1;
|
||||||
|
case 10: // 1-year digit
|
||||||
|
return (Time->tm_year % 10) & 0xF;
|
||||||
|
case 11: // 10-years digit
|
||||||
|
return ((Time->tm_year % 100) / 10) & 0xF;
|
||||||
|
case 12: // day of the week
|
||||||
|
return Time->tm_wday & 0x7;
|
||||||
|
case 13:
|
||||||
|
break;
|
||||||
|
case 14:
|
||||||
|
break;
|
||||||
|
case 15:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CRTC72421::WriteRegister(unsigned reg, UINT8 data)
|
||||||
|
{
|
||||||
|
// TO-DO: emulate me!
|
||||||
|
}
|
||||||
|
|
||||||
|
void CRTC72421::Reset(void)
|
||||||
|
{
|
||||||
|
// nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Initialization and Shutdown
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void CRTC72421::Init(void)
|
||||||
|
{
|
||||||
|
// this function really only exists for consistency with other device classes
|
||||||
|
}
|
||||||
|
|
||||||
|
CRTC72421::CRTC72421(void)
|
||||||
|
{
|
||||||
|
DebugLog("Built RTC-72421\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
CRTC72421::~CRTC72421(void)
|
||||||
|
{
|
||||||
|
DebugLog("Destroyed RTC-72421\n");
|
||||||
|
}
|
91
Model3/RTC72421.h
Normal file
91
Model3/RTC72421.h
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RTC72421.h
|
||||||
|
*
|
||||||
|
* Header file defining the CRTC72421 class: RTC-72421 real-time clock.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDED_RTC72421_H
|
||||||
|
#define INCLUDED_RTC72421_H
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CRTC72421:
|
||||||
|
*
|
||||||
|
* Epson RTC-72421 real-time clock.
|
||||||
|
*/
|
||||||
|
class CRTC72421
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
* ReadRegister(unsigned reg):
|
||||||
|
*
|
||||||
|
* Reads data from one of the RTC registers.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* reg Register number (0-15).
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Data in the lower nibble.
|
||||||
|
*/
|
||||||
|
UINT8 ReadRegister(unsigned reg);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WriteRegister(unsigned reg, UINT8 data):
|
||||||
|
*
|
||||||
|
* Writes data to one of the RTC registers.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* reg Register number (0-15).
|
||||||
|
* data Data to write.
|
||||||
|
*/
|
||||||
|
void WriteRegister(unsigned reg, UINT8 data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset(void):
|
||||||
|
*
|
||||||
|
* Does nothing. Provided for compability with other device objects. The
|
||||||
|
* RTC is battery-backed and always available.
|
||||||
|
*/
|
||||||
|
void Reset(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Init(void):
|
||||||
|
*
|
||||||
|
* One-time initialization of the context. Must be called prior to all
|
||||||
|
* other members.
|
||||||
|
*/
|
||||||
|
void Init(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CRTC72421(void):
|
||||||
|
* ~CRTC72421(void):
|
||||||
|
*
|
||||||
|
* Constructor and destructor.
|
||||||
|
*/
|
||||||
|
CRTC72421(void);
|
||||||
|
~CRTC72421(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // INCLUDED_RTC72421_H
|
945
Model3/Real3D.cpp
Normal file
945
Model3/Real3D.cpp
Normal file
|
@ -0,0 +1,945 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Real3D.cpp
|
||||||
|
*
|
||||||
|
* The Model 3's Real3D-based graphics hardware. Based on the Real3D Pro-1000
|
||||||
|
* family of image generators.
|
||||||
|
*
|
||||||
|
* PCI IDs
|
||||||
|
* -------
|
||||||
|
* It appears that Step 2.0 returns a different PCI ID depending on whether
|
||||||
|
* the PCI configuration space or DMA register are accessed. For example,
|
||||||
|
* Virtual On 2 expects 0x178611DB from the PCI configuration header but
|
||||||
|
* 0x16C311DB from the DMA device.
|
||||||
|
*
|
||||||
|
* To-Do List
|
||||||
|
* ----------
|
||||||
|
* - For consistency, the status registers should probably be byte reversed (this is a
|
||||||
|
* little endian device), forcing the Model3 Read32/Write32 handlers to
|
||||||
|
* manually reverse the data. This keeps with the convention for VRAM.
|
||||||
|
* - Keep an eye out for games writing non-mipmap textures to the mipmap area.
|
||||||
|
* The render currently cannot cope with this.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include "Supermodel.h"
|
||||||
|
|
||||||
|
// Offsets of memory regions within Real3D memory pool
|
||||||
|
#define OFFSET_8C 0 // 4 MB, culling RAM low (at 0x8C000000)
|
||||||
|
#define OFFSET_8E 0x400000 // 1 MB, culling RAM high (at 0x8E000000)
|
||||||
|
#define OFFSET_98 0x500000 // 4 MB, polygon RAM (at 0x98000000)
|
||||||
|
#define OFFSET_TEXRAM 0x900000 // 8 MB, texture RAM
|
||||||
|
#define OFFSET_TEXFIFO 0x1100000 // 1 MB, texture FIFO
|
||||||
|
#define MEMORY_POOL_SIZE (0x400000+0x100000+0x400000+0x800000+0x100000)
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Save States
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void CReal3D::SaveState(CBlockFile *SaveState)
|
||||||
|
{
|
||||||
|
SaveState->NewBlock("Real3D", __FILE__);
|
||||||
|
|
||||||
|
SaveState->Write(memoryPool, MEMORY_POOL_SIZE);
|
||||||
|
SaveState->Write(&fifoIdx, sizeof(fifoIdx));
|
||||||
|
SaveState->Write(&vromTextureAddr, sizeof(vromTextureAddr));
|
||||||
|
SaveState->Write(&vromTextureHeader, sizeof(vromTextureHeader));
|
||||||
|
|
||||||
|
SaveState->Write(&dmaSrc, sizeof(dmaSrc));
|
||||||
|
SaveState->Write(&dmaDest, sizeof(dmaDest));
|
||||||
|
SaveState->Write(&dmaLength, sizeof(dmaLength));
|
||||||
|
SaveState->Write(&dmaData, sizeof(dmaData));
|
||||||
|
SaveState->Write(&dmaUnknownReg, sizeof(dmaUnknownReg));
|
||||||
|
SaveState->Write(&dmaStatus, sizeof(dmaStatus));
|
||||||
|
SaveState->Write(&dmaConfig, sizeof(dmaConfig));
|
||||||
|
|
||||||
|
SaveState->Write(&tapCurrentInstruction, sizeof(tapCurrentInstruction));
|
||||||
|
SaveState->Write(&tapIR, sizeof(tapIR));
|
||||||
|
SaveState->Write(tapID, sizeof(tapID));
|
||||||
|
SaveState->Write(&tapIDSize, sizeof(tapIDSize));
|
||||||
|
SaveState->Write(&tapTDO, sizeof(tapTDO));
|
||||||
|
SaveState->Write(&tapState, sizeof(tapState));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CReal3D::LoadState(CBlockFile *SaveState)
|
||||||
|
{
|
||||||
|
if (OKAY != SaveState->FindBlock("Real3D"))
|
||||||
|
{
|
||||||
|
ErrorLog("Unable to load Real3D GPU state. Save state file is corrupted.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveState->Read(memoryPool, MEMORY_POOL_SIZE);
|
||||||
|
Render3D->UploadTextures(0,0,2048,2048);
|
||||||
|
SaveState->Read(&fifoIdx, sizeof(fifoIdx));
|
||||||
|
SaveState->Read(&vromTextureAddr, sizeof(vromTextureAddr));
|
||||||
|
SaveState->Read(&vromTextureHeader, sizeof(vromTextureHeader));
|
||||||
|
|
||||||
|
SaveState->Read(&dmaSrc, sizeof(dmaSrc));
|
||||||
|
SaveState->Read(&dmaDest, sizeof(dmaDest));
|
||||||
|
SaveState->Read(&dmaLength, sizeof(dmaLength));
|
||||||
|
SaveState->Read(&dmaData, sizeof(dmaData));
|
||||||
|
SaveState->Read(&dmaUnknownReg, sizeof(dmaUnknownReg));
|
||||||
|
SaveState->Read(&dmaStatus, sizeof(dmaStatus));
|
||||||
|
SaveState->Read(&dmaConfig, sizeof(dmaConfig));
|
||||||
|
|
||||||
|
SaveState->Read(&tapCurrentInstruction, sizeof(tapCurrentInstruction));
|
||||||
|
SaveState->Read(&tapIR, sizeof(tapIR));
|
||||||
|
SaveState->Read(tapID, sizeof(tapID));
|
||||||
|
SaveState->Read(&tapIDSize, sizeof(tapIDSize));
|
||||||
|
SaveState->Read(&tapTDO, sizeof(tapTDO));
|
||||||
|
SaveState->Read(&tapState, sizeof(tapState));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Rendering
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void CReal3D::RenderFrame(void)
|
||||||
|
{
|
||||||
|
//if (commandPortWritten)
|
||||||
|
Render3D->RenderFrame();
|
||||||
|
commandPortWritten = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CReal3D::BeginFrame(void)
|
||||||
|
{
|
||||||
|
status |= 2; // VBlank bit
|
||||||
|
Render3D->BeginFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CReal3D::EndFrame(void)
|
||||||
|
{
|
||||||
|
status &= ~2;
|
||||||
|
Render3D->EndFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
DMA Device
|
||||||
|
|
||||||
|
Register 0xC:
|
||||||
|
-------------
|
||||||
|
+---+---+---+---+---+---+---+---+
|
||||||
|
|BUS|???|???|???|???|???|???|IRQ|
|
||||||
|
+---+---+---+---+---+---+---+---+
|
||||||
|
BUS: Busy (see von2 0x18A104) if 1.
|
||||||
|
IRQ: IRQ pending.
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void CReal3D::DMACopy(void)
|
||||||
|
{
|
||||||
|
DebugLog("Real3D DMA copy (PC=%08X, LR=%08X): %08X -> %08X, %X %s\n", ppc_get_pc(), ppc_get_lr(), dmaSrc, dmaDest, dmaLength*4, (dmaConfig&0x80)?"(byte reversed)":"");
|
||||||
|
//printf("Real3D DMA copy (PC=%08X, LR=%08X): %08X -> %08X, %X %s\n", ppc_get_pc(), ppc_get_lr(), dmaSrc, dmaDest, dmaLength*4, (dmaConfig&0x80)?"(byte reversed)":"");
|
||||||
|
if ((dmaConfig&0x80)) // reverse bytes
|
||||||
|
{
|
||||||
|
while (dmaLength != 0)
|
||||||
|
{
|
||||||
|
UINT32 data = Bus->Read32(dmaSrc);
|
||||||
|
Bus->Write32(dmaDest, FLIPENDIAN32(data));
|
||||||
|
dmaSrc += 4;
|
||||||
|
dmaDest += 4;
|
||||||
|
--dmaLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while (dmaLength != 0)
|
||||||
|
{
|
||||||
|
Bus->Write32(dmaDest, Bus->Read32(dmaSrc));
|
||||||
|
dmaSrc += 4;
|
||||||
|
dmaDest += 4;
|
||||||
|
--dmaLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT8 CReal3D::ReadDMARegister8(unsigned reg)
|
||||||
|
{
|
||||||
|
switch (reg)
|
||||||
|
{
|
||||||
|
case 0xC: // status
|
||||||
|
return dmaStatus;
|
||||||
|
case 0xE: // configuration
|
||||||
|
return dmaConfig;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugLog("Real3D: ReadDMARegister8: reg=%X\n", reg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CReal3D::WriteDMARegister8(unsigned reg, UINT8 data)
|
||||||
|
{
|
||||||
|
switch (reg)
|
||||||
|
{
|
||||||
|
case 0xD: // IRQ acknowledge
|
||||||
|
if ((data&1))
|
||||||
|
{
|
||||||
|
dmaStatus &= ~1;
|
||||||
|
IRQ->Deassert(dmaIRQ);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0xE: // configuration
|
||||||
|
dmaConfig = data;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DebugLog("Real3D: WriteDMARegister8: reg=%X, data=%02X\n", reg, data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
//DebugLog("Real3D: WriteDMARegister8: reg=%X, data=%02X\n", reg, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT32 CReal3D::ReadDMARegister32(unsigned reg)
|
||||||
|
{
|
||||||
|
switch (reg)
|
||||||
|
{
|
||||||
|
case 0x14: // command result
|
||||||
|
return dmaData;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugLog("Real3D: ReadDMARegister32: reg=%X\n", reg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CReal3D::WriteDMARegister32(unsigned reg, UINT32 data)
|
||||||
|
{
|
||||||
|
switch (reg)
|
||||||
|
{
|
||||||
|
case 0x00: // DMA source address
|
||||||
|
dmaSrc = data;
|
||||||
|
break;
|
||||||
|
case 0x04: // DMA destination address
|
||||||
|
dmaDest = data;
|
||||||
|
break;
|
||||||
|
case 0x08: // DMA length
|
||||||
|
dmaLength = data;
|
||||||
|
DMACopy();
|
||||||
|
dmaStatus |= 1;
|
||||||
|
IRQ->Assert(dmaIRQ);
|
||||||
|
break;
|
||||||
|
case 0x10: // command register
|
||||||
|
if ((data&0x20000000))
|
||||||
|
{
|
||||||
|
dmaData = 0x16C311DB; // Virtual On 2 expects this from DMA
|
||||||
|
DebugLog("Real3D: DMA ID command issued (ATTENTION: make sure we're returning the correct value), PC=%08X, LR=%08X\n", ppc_get_pc(), ppc_get_lr());
|
||||||
|
}
|
||||||
|
else if ((data&0x80000000))
|
||||||
|
{
|
||||||
|
dmaUnknownReg ^= 0xFFFFFFFF;
|
||||||
|
dmaData = dmaUnknownReg;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x14: // ?
|
||||||
|
dmaData = 0xFFFFFFFF;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DebugLog("Real3D: WriteDMARegister32: reg=%X, data=%08X\n", reg, data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
//DebugLog("Real3D: WriteDMARegister32: reg=%X, data=%08X\n", reg, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
JTAG Test Access Port Simulation
|
||||||
|
|
||||||
|
What I term as "IDs" here are really boundary scan values.
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
static const int tapFSM[][2] = // finite state machine, each state can lead to 2 next states
|
||||||
|
{
|
||||||
|
{ 1, 0 }, // 0 Test-Logic/Reset
|
||||||
|
{ 1, 2 }, // 1 Run-Test/Idle
|
||||||
|
{ 3, 9 }, // 2 Select-DR-Scan
|
||||||
|
{ 4, 5 }, // 3 Capture-DR
|
||||||
|
{ 4, 5 }, // 4 Shift-DR
|
||||||
|
{ 6, 8 }, // 5 Exit1-DR
|
||||||
|
{ 6, 7 }, // 6 Pause-DR
|
||||||
|
{ 4, 8 }, // 7 Exit2-DR
|
||||||
|
{ 1, 2 }, // 8 Update-DR
|
||||||
|
{ 10, 0 }, // 9 Select-IR-Scan
|
||||||
|
{ 11, 12 }, // 10 Capture-IR
|
||||||
|
{ 11, 12 }, // 11 Shift-IR
|
||||||
|
{ 13, 15 }, // 12 Exit1-IR
|
||||||
|
{ 13, 14 }, // 13 Pause-IR
|
||||||
|
{ 11, 15 }, // 14 Exit2-IR
|
||||||
|
{ 1, 2 } // 15 Update-IR
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* InsertBit():
|
||||||
|
*
|
||||||
|
* Inserts a bit into an arbitrarily long bit field. Bit 0 is assumed to be
|
||||||
|
* the MSB of the first byte in the buffer.
|
||||||
|
*/
|
||||||
|
void CReal3D::InsertBit(UINT8 *buf, unsigned bitNum, unsigned bit)
|
||||||
|
{
|
||||||
|
unsigned bitInByte;
|
||||||
|
|
||||||
|
bitInByte = 7 - (bitNum & 7);
|
||||||
|
|
||||||
|
buf[bitNum / 8] &= ~(1 << bitInByte);
|
||||||
|
buf[bitNum / 8] |= (bit << bitInByte);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* InsertID():
|
||||||
|
*
|
||||||
|
* Inserts a 32-bit ID code into the ID bit field.
|
||||||
|
*/
|
||||||
|
void CReal3D::InsertID(UINT32 id, unsigned startBit)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 31; i >= 0; i--)
|
||||||
|
InsertBit(tapID, startBit++, (id >> i) & 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Shift():
|
||||||
|
*
|
||||||
|
* Shifts the data buffer right (towards LSB at byte 0) by 1 bit. The size of
|
||||||
|
* the number of bits must be specified. The bit shifted out of the LSB is
|
||||||
|
* returned.
|
||||||
|
*/
|
||||||
|
unsigned CReal3D::Shift(UINT8 *data, unsigned numBits)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
unsigned shiftOut, shiftIn;
|
||||||
|
|
||||||
|
// This loop takes care of all the fully-filled bytes
|
||||||
|
shiftIn = 0;
|
||||||
|
shiftOut = 0;
|
||||||
|
for (i = 0; i < numBits / 8; i++)
|
||||||
|
{
|
||||||
|
shiftOut = data[i] & 1;
|
||||||
|
data[i] >>= 1;
|
||||||
|
data[i] |= (shiftIn << 7);
|
||||||
|
shiftIn = shiftOut; // carry over to next element's MSB
|
||||||
|
}
|
||||||
|
|
||||||
|
// Take care of the last partial byte (if there is one)
|
||||||
|
if ((numBits & 7) != 0)
|
||||||
|
{
|
||||||
|
shiftOut = (data[i] >> (8 - (numBits & 7))) & 1;
|
||||||
|
data[i] >>= 1;
|
||||||
|
data[i] |= (shiftIn << 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
return shiftOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned CReal3D::ReadTAP(void)
|
||||||
|
{
|
||||||
|
return tapTDO;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CReal3D::WriteTAP(unsigned tck, unsigned tms, unsigned tdi, unsigned trst)
|
||||||
|
{
|
||||||
|
if (!tck)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Go to next state
|
||||||
|
tapState = tapFSM[tapState][tms];
|
||||||
|
|
||||||
|
switch (tapState)
|
||||||
|
{
|
||||||
|
case 3: // Capture-DR
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read ASIC IDs.
|
||||||
|
*
|
||||||
|
* The ID Sequence is:
|
||||||
|
* - Jupiter
|
||||||
|
* - Mercury
|
||||||
|
* - Venus
|
||||||
|
* - Earth
|
||||||
|
* - Mars
|
||||||
|
* - Mars (again)
|
||||||
|
*
|
||||||
|
* Note that different Model 3 steps have different chip
|
||||||
|
* revisions, hence the different IDs returned below.
|
||||||
|
*
|
||||||
|
* On Step 1.5 and 1.0, instruction 0x0C631F8C7FFE is used to retrieve
|
||||||
|
* the ID codes but Step 2.0 is a little weirder. It seems to use this
|
||||||
|
* and either the state of the TAP after reset or other instructions
|
||||||
|
* to read the IDs as well. This can be emulated in one of 2 ways:
|
||||||
|
* Ignore the instruction and always load up the data or load the
|
||||||
|
* data on TAP reset and when the instruction is issued.
|
||||||
|
*/
|
||||||
|
if (step == 0x10)
|
||||||
|
{
|
||||||
|
InsertID(0x116C7057, 1 + 0 * 32);
|
||||||
|
InsertID(0x216C3057, 1 + 1 * 32);
|
||||||
|
InsertID(0x116C4057, 1 + 2 * 32);
|
||||||
|
InsertID(0x216C5057, 1 + 3 * 32);
|
||||||
|
InsertID(0x116C6057, 1 + 4 * 32 + 1);
|
||||||
|
InsertID(0x116C6057, 1 + 5 * 32 + 1);
|
||||||
|
}
|
||||||
|
else if (step == 0x15)
|
||||||
|
{
|
||||||
|
InsertID(0x316C7057, 1 + 0 * 32);
|
||||||
|
InsertID(0x316C3057, 1 + 1 * 32);
|
||||||
|
InsertID(0x216C4057, 1 + 2 * 32); // Lost World may to use 0x016C4057
|
||||||
|
InsertID(0x316C5057, 1 + 3 * 32);
|
||||||
|
InsertID(0x216C6057, 1 + 4 * 32 + 1);
|
||||||
|
InsertID(0x216C6057, 1 + 5 * 32 + 1);
|
||||||
|
}
|
||||||
|
else if (step >= 0x20)
|
||||||
|
{
|
||||||
|
InsertID(0x416C7057, 1 + 0 * 32);
|
||||||
|
InsertID(0x416C3057, 1 + 1 * 32);
|
||||||
|
InsertID(0x316C4057, 1 + 2 * 32);
|
||||||
|
InsertID(0x416C5057, 1 + 3 * 32);
|
||||||
|
InsertID(0x316C6057, 1 + 4 * 32 + 1);
|
||||||
|
InsertID(0x316C6057, 1 + 5 * 32 + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 4: // Shift-DR
|
||||||
|
|
||||||
|
tapTDO = Shift(tapID, tapIDSize);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 10: // Capture-IR
|
||||||
|
|
||||||
|
// Load lower 2 bits with 01 as per IEEE 1149.1-1990
|
||||||
|
tapIR = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 11: // Shift-IR
|
||||||
|
|
||||||
|
// Shift IR towards output and load in new data from TDI
|
||||||
|
tapTDO = tapIR & 1; // shift LSB to output
|
||||||
|
tapIR >>= 1;
|
||||||
|
tapIR |= ((UINT64) tdi << 45);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 15: // Update-IR
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Latch IR (technically, this should occur on the falling edge of
|
||||||
|
* TCK)
|
||||||
|
*/
|
||||||
|
tapIR &= 0x3FFFFFFFFFFFULL;
|
||||||
|
tapCurrentInstruction = tapIR;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Texture Uploading and Decoding
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
// Mipmap coordinates for each reduction level (within a single 2048x1024 page)
|
||||||
|
static const int mipXBase[11] =
|
||||||
|
{
|
||||||
|
1024, // 1024/2
|
||||||
|
1536, // 512/2
|
||||||
|
1792, // 256/2
|
||||||
|
1920, // ...
|
||||||
|
1984,
|
||||||
|
2016,
|
||||||
|
2032,
|
||||||
|
2040,
|
||||||
|
2044,
|
||||||
|
2046,
|
||||||
|
2047
|
||||||
|
};
|
||||||
|
|
||||||
|
static const int mipYBase[11] =
|
||||||
|
{
|
||||||
|
512,
|
||||||
|
768,
|
||||||
|
896,
|
||||||
|
960,
|
||||||
|
992,
|
||||||
|
1008,
|
||||||
|
1016,
|
||||||
|
1020,
|
||||||
|
1022,
|
||||||
|
1023,
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mipmap reduction factors
|
||||||
|
static const int mipDivisor[9] = { 2, 4, 8, 16, 32, 64, 128, 256, 512 };
|
||||||
|
|
||||||
|
// Table of texel offsets corresponding to an 8x8 texel texture tile
|
||||||
|
static const unsigned decode[64] =
|
||||||
|
{
|
||||||
|
0, 1, 4, 5, 8, 9,12,13,
|
||||||
|
2, 3, 6, 7,10,11,14,15,
|
||||||
|
16,17,20,21,24,25,28,29,
|
||||||
|
18,19,22,23,26,27,30,31,
|
||||||
|
32,33,36,37,40,41,44,45,
|
||||||
|
34,35,38,39,42,43,46,47,
|
||||||
|
48,49,52,53,56,57,60,61,
|
||||||
|
50,51,54,55,58,59,62,63
|
||||||
|
};
|
||||||
|
|
||||||
|
void CReal3D::StoreTexture(unsigned xPos, unsigned yPos, unsigned width, unsigned height, UINT16 *texData, unsigned bytesPerTexel)
|
||||||
|
{
|
||||||
|
unsigned x, y, xx, yy, destOffset;
|
||||||
|
|
||||||
|
if (bytesPerTexel == 2) // 16-bit textures
|
||||||
|
{
|
||||||
|
// Outer 2 loops: 8x8 tiles
|
||||||
|
for (y = yPos; y < (yPos+height); y += 8)
|
||||||
|
{
|
||||||
|
for (x = xPos; x < (xPos+width); x += 8)
|
||||||
|
{
|
||||||
|
// Inner 2 loops: 8x8 texels for the current tile
|
||||||
|
destOffset = y*2048+x;
|
||||||
|
for (yy = 0; yy < 8; yy++)
|
||||||
|
{
|
||||||
|
for (xx = 0; xx < 8; xx++)
|
||||||
|
textureRAM[destOffset++] = texData[decode[(yy*8+xx)^1]];
|
||||||
|
destOffset += 2048-8; // next line
|
||||||
|
}
|
||||||
|
texData += 8*8; // next tile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // 8-bit textures
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* 8-bit textures appear to be unpacked into 16-bit words in the
|
||||||
|
* texture RAM. Oddly, the rows of the decoding table seem to be
|
||||||
|
* swapped.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Outer 2 loops: 8x8 tiles
|
||||||
|
for (y = yPos; y < (yPos+height); y += 8)
|
||||||
|
{
|
||||||
|
for (x = xPos; x < (xPos+width); x += 8)
|
||||||
|
{
|
||||||
|
// Inner 2 loops: 8x8 texels for the current tile
|
||||||
|
destOffset = y*2048+x;
|
||||||
|
for (yy = 0; yy < 8; yy++)
|
||||||
|
{
|
||||||
|
for (xx = 0; xx < 8; xx += 2)
|
||||||
|
{
|
||||||
|
textureRAM[destOffset++] = texData[decode[(yy^1)*8+((xx+0)^1)]/2]>>8;
|
||||||
|
textureRAM[destOffset++] = texData[decode[(yy^1)*8+((xx+1)^1)]/2]&0xFF;
|
||||||
|
|
||||||
|
}
|
||||||
|
destOffset += 2048-8;
|
||||||
|
}
|
||||||
|
texData += 8*8/2; // next tile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Texture data will be in little endian format
|
||||||
|
void CReal3D::UploadTexture(UINT32 header, UINT16 *texData)
|
||||||
|
{
|
||||||
|
unsigned x, y, page, width, height, bytesPerTexel,
|
||||||
|
mipYPos, mipWidth, mipHeight, mipNum, mipX, mipY;
|
||||||
|
|
||||||
|
|
||||||
|
// Position: texture RAM is arranged as 2 2048x1024 texel sheets
|
||||||
|
x = 32*(header&0x3F);
|
||||||
|
y = 32*((header>>7)&0x1F);
|
||||||
|
page = (header>>20)&1;
|
||||||
|
y += page*1024; // treat page as additional Y bit (one 2048x2048 sheet)
|
||||||
|
|
||||||
|
// Texture size and bit depth
|
||||||
|
width = 32<<((header>>14)&7);
|
||||||
|
height = 32<<((header>>17)&7);
|
||||||
|
if ((header&0x00800000)) // 16 bits per texel
|
||||||
|
bytesPerTexel = 2;
|
||||||
|
else // 8 bits
|
||||||
|
{
|
||||||
|
bytesPerTexel = 1;
|
||||||
|
//printf("8-bit textures!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mipmaps
|
||||||
|
mipYPos = 32*((header>>7)&0x1F);
|
||||||
|
|
||||||
|
// Process texture data
|
||||||
|
DebugLog("Real3D: Texture upload: pos=(%d,%d) size=(%d,%d), %d-bit\n", x, y, width, height, bytesPerTexel*8);
|
||||||
|
//printf("Real3D: Texture upload: pos=(%d,%d) size=(%d,%d), %d-bit\n", x, y, width, height, bytesPerTexel*8);
|
||||||
|
switch ((header>>24)&0x0F)
|
||||||
|
{
|
||||||
|
case 0x00: // texture w/ mipmaps
|
||||||
|
StoreTexture(x, y, width, height, texData, bytesPerTexel);
|
||||||
|
mipWidth = width;
|
||||||
|
mipHeight = height;
|
||||||
|
mipNum = 0;
|
||||||
|
|
||||||
|
while((mipHeight>8) && (mipWidth>8))
|
||||||
|
{
|
||||||
|
if (bytesPerTexel == 1)
|
||||||
|
texData += (mipWidth*mipHeight)/2;
|
||||||
|
else
|
||||||
|
texData += (mipWidth*mipHeight);
|
||||||
|
mipWidth /= 2;
|
||||||
|
mipHeight /= 2;
|
||||||
|
mipX = mipXBase[mipNum] + (x / mipDivisor[mipNum]);
|
||||||
|
mipY = mipYBase[mipNum] + (mipYPos / mipDivisor[mipNum]);
|
||||||
|
if(page)
|
||||||
|
mipY += 1024;
|
||||||
|
mipNum++;
|
||||||
|
StoreTexture(mipX, mipY, mipWidth, mipHeight, (UINT16 *) texData, bytesPerTexel);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 0x01: // texture w/out mipmaps
|
||||||
|
StoreTexture(x, y, width, height, texData, bytesPerTexel);
|
||||||
|
break;
|
||||||
|
case 0x02: // mipmaps only
|
||||||
|
mipWidth = width;
|
||||||
|
mipHeight = height;
|
||||||
|
mipNum = 0;
|
||||||
|
while((mipHeight>8) && (mipWidth>8))
|
||||||
|
{
|
||||||
|
mipWidth /= 2;
|
||||||
|
mipHeight /= 2;
|
||||||
|
mipX = mipXBase[mipNum] + (x / mipDivisor[mipNum]);
|
||||||
|
mipY = mipYBase[mipNum] + (mipYPos / mipDivisor[mipNum]);
|
||||||
|
if(page)
|
||||||
|
mipY += 1024;
|
||||||
|
mipNum++;
|
||||||
|
StoreTexture(mipX, mipY, mipWidth, mipHeight, texData, bytesPerTexel);
|
||||||
|
if (bytesPerTexel == 1)
|
||||||
|
texData += (mipWidth*mipHeight)/2;
|
||||||
|
else
|
||||||
|
texData += (mipWidth*mipHeight);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x80: // MAME thinks these might be a gamma table
|
||||||
|
//break;
|
||||||
|
default: // unknown
|
||||||
|
printf("unknown texture format %02X\n", header>>24);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signal to renderer that textures have changed
|
||||||
|
// TO-DO: mipmaps? What if a game writes non-mipmap textures to mipmap area?
|
||||||
|
//Render3D->UploadTextures(x,y,width,height);
|
||||||
|
Render3D->UploadTextures(0,0,2048,2048); // TO-DO: should not have to upload all 2048x2048 texels
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Basic Emulation Functions, Registers, Memory, and Texture FIFO
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void CReal3D::Flush(void)
|
||||||
|
{
|
||||||
|
unsigned i, size;
|
||||||
|
UINT32 header;
|
||||||
|
|
||||||
|
commandPortWritten = TRUE;
|
||||||
|
|
||||||
|
DebugLog("Real3D 88000000 written @ PC=%08X\n", ppc_get_pc());
|
||||||
|
|
||||||
|
// Upload textures (if any)
|
||||||
|
if (fifoIdx > 0)
|
||||||
|
{
|
||||||
|
for (i = 0; i < fifoIdx; )
|
||||||
|
{
|
||||||
|
size = 2+textureFIFO[i+0]/2;
|
||||||
|
size /= 4;
|
||||||
|
header = textureFIFO[i+1]; // texture information header
|
||||||
|
UploadTexture(header,(UINT16 *)&textureFIFO[i+2]);
|
||||||
|
DebugLog("Real3D: Texture upload completed: %X bytes (%X)\n", size*4, textureFIFO[i+0]);
|
||||||
|
i += size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset texture FIFO
|
||||||
|
fifoIdx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CReal3D::WriteTextureFIFO(UINT32 data)
|
||||||
|
{
|
||||||
|
textureFIFO[fifoIdx++] = data;
|
||||||
|
if (fifoIdx >= (0x100000/4))
|
||||||
|
ErrorLog("Real3D texture FIFO maxed out!");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CReal3D::WriteTexturePort(unsigned reg, UINT32 data)
|
||||||
|
{
|
||||||
|
//printf("Texture Port: %X=%08X\n", reg, data);
|
||||||
|
switch (reg)
|
||||||
|
{
|
||||||
|
case 0x0: // VROM texture address
|
||||||
|
case 0xC:
|
||||||
|
vromTextureAddr = data;
|
||||||
|
break;
|
||||||
|
case 0x4: // VROM texture header
|
||||||
|
case 0x10:
|
||||||
|
vromTextureHeader = data;
|
||||||
|
break;
|
||||||
|
case 0x8: // VROM texture length (also used to trigger uploads)
|
||||||
|
case 0x14:
|
||||||
|
UploadTexture(vromTextureHeader,(UINT16 *)&vrom[vromTextureAddr&0xFFFFFF]);
|
||||||
|
//printf("texture upload: addr=%08X\n", vromTextureAddr);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DebugLog("Real3D texture port write: %X=%08X\n", reg, data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CReal3D::WriteLowCullingRAM(UINT32 addr, UINT32 data)
|
||||||
|
{
|
||||||
|
cullingRAMLo[addr/4] = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CReal3D::WriteHighCullingRAM(UINT32 addr, UINT32 data)
|
||||||
|
{
|
||||||
|
cullingRAMHi[addr/4] = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CReal3D::WritePolygonRAM(UINT32 addr, UINT32 data)
|
||||||
|
{
|
||||||
|
polyRAM[addr/4] = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Registers seem to range from 0x00 to around 0x3C but they are not understood
|
||||||
|
UINT32 CReal3D::ReadRegister(unsigned reg)
|
||||||
|
{
|
||||||
|
DebugLog("Real3D: Read reg %X\n", reg);
|
||||||
|
if (reg == 0)
|
||||||
|
return 0xFFFFFFFD|status;
|
||||||
|
else
|
||||||
|
return 0xFFFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT32 CReal3D::ReadPCIConfigSpace(unsigned device, unsigned reg, unsigned bits, unsigned offset)
|
||||||
|
{
|
||||||
|
UINT32 d;
|
||||||
|
|
||||||
|
if ((bits==8))
|
||||||
|
{
|
||||||
|
DebugLog("Real3D: %d-bit PCI read request for reg=%02X\n", bits, reg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a little endian device, must return little endian words
|
||||||
|
switch (reg)
|
||||||
|
{
|
||||||
|
case 0x00: // Device ID and Vendor ID
|
||||||
|
d = FLIPENDIAN32(pciID);
|
||||||
|
switch (bits)
|
||||||
|
{
|
||||||
|
case 8:
|
||||||
|
d >>= (3-offset)*8; // offset will be 0-3; select appropriate byte
|
||||||
|
d &= 0xFF;
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
d >>= (2-offset)*8; // offset will be 0 or 2 only; select either high or low word
|
||||||
|
d &= 0xFFFF;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
DebugLog("Real3D: PCI ID read. Returning %X (%d-bits). PC=%08X, LR=%08X\n", d, bits, ppc_get_pc(), ppc_get_lr());
|
||||||
|
return d;
|
||||||
|
default:
|
||||||
|
DebugLog("Real3D: PCI read request for reg=%02X (%d-bit)\n", reg, bits);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CReal3D::WritePCIConfigSpace(unsigned device, unsigned reg, unsigned bits, unsigned offset, UINT32 data)
|
||||||
|
{
|
||||||
|
DebugLog("Real3D: PCI %d-bit write request for reg=%02X, data=%08X\n", bits, reg, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CReal3D::Reset(void)
|
||||||
|
{
|
||||||
|
commandPortWritten = FALSE;
|
||||||
|
|
||||||
|
fifoIdx = 0;
|
||||||
|
status = 0;
|
||||||
|
vromTextureAddr = 0;
|
||||||
|
vromTextureHeader = 0;
|
||||||
|
tapState = 0;
|
||||||
|
tapIDSize = 197;
|
||||||
|
dmaStatus = 0;
|
||||||
|
dmaUnknownReg = 0;
|
||||||
|
|
||||||
|
memset(memoryPool, 0, MEMORY_POOL_SIZE);
|
||||||
|
|
||||||
|
DebugLog("Real3D reset\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Configuration, Initialization, and Shutdown
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void CReal3D::AttachRenderer(CRender3D *Render3DPtr)
|
||||||
|
{
|
||||||
|
Render3D = Render3DPtr;
|
||||||
|
Render3D->AttachMemory(cullingRAMLo,cullingRAMHi,polyRAM,vrom,textureRAM);
|
||||||
|
Render3D->SetStep(step);
|
||||||
|
DebugLog("Real3D attached a Render3D object\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CReal3D::SetStep(int stepID)
|
||||||
|
{
|
||||||
|
step = stepID;
|
||||||
|
if ((step!=0x10) && (step!=0x15) && (step!=0x20) && (step!=0x21))
|
||||||
|
{
|
||||||
|
DebugLog("Real3D: Unrecognized stepping: %d.%d\n", (step>>4)&0xF, step&0xF);
|
||||||
|
step = 0x10;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set PCI ID
|
||||||
|
if (step < 0x20)
|
||||||
|
pciID = 0x16C311DB; // vendor 0x11DB = Sega
|
||||||
|
else
|
||||||
|
pciID = 0x178611DB;
|
||||||
|
|
||||||
|
// Pass to renderer
|
||||||
|
if (Render3D != NULL)
|
||||||
|
Render3D->SetStep(step);
|
||||||
|
|
||||||
|
DebugLog("Real3D set to Step %d.%d\n", (step>>4)&0xF, step&0xF);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL CReal3D::Init(const UINT8 *vromPtr, CBus *BusObjectPtr, CIRQ *IRQObjectPtr, unsigned dmaIRQBit)
|
||||||
|
{
|
||||||
|
float memSizeMB = (float)MEMORY_POOL_SIZE/(float)0x100000;
|
||||||
|
|
||||||
|
// IRQ and bus objects
|
||||||
|
Bus = BusObjectPtr;
|
||||||
|
IRQ = IRQObjectPtr;
|
||||||
|
dmaIRQ = dmaIRQBit;
|
||||||
|
|
||||||
|
// Allocate all Real3D RAM regions
|
||||||
|
memoryPool = new(std::nothrow) UINT8[MEMORY_POOL_SIZE];
|
||||||
|
if (NULL == memoryPool)
|
||||||
|
return ErrorLog("Insufficient memory for Real3D object (needs %1.1f MB).", memSizeMB);
|
||||||
|
|
||||||
|
// Set up pointers
|
||||||
|
cullingRAMLo = (UINT32 *) &memoryPool[OFFSET_8C];
|
||||||
|
cullingRAMHi = (UINT32 *) &memoryPool[OFFSET_8E];
|
||||||
|
polyRAM = (UINT32 *) &memoryPool[OFFSET_98];
|
||||||
|
textureRAM = (UINT16 *) &memoryPool[OFFSET_TEXRAM];
|
||||||
|
textureFIFO = (UINT32 *) &memoryPool[OFFSET_TEXFIFO];
|
||||||
|
|
||||||
|
// VROM pointer passed to us
|
||||||
|
vrom = (UINT32 *) vromPtr;
|
||||||
|
|
||||||
|
return OKAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
CReal3D::CReal3D(void)
|
||||||
|
{
|
||||||
|
Render3D = NULL;
|
||||||
|
memoryPool = NULL;
|
||||||
|
cullingRAMLo = NULL;
|
||||||
|
cullingRAMHi = NULL;
|
||||||
|
polyRAM = NULL;
|
||||||
|
textureRAM = NULL;
|
||||||
|
textureFIFO = NULL;
|
||||||
|
vrom = NULL;
|
||||||
|
fifoIdx = 0;
|
||||||
|
vromTextureAddr = 0;
|
||||||
|
vromTextureHeader = 0;
|
||||||
|
tapState = 0;
|
||||||
|
tapIDSize = 197;
|
||||||
|
DebugLog("Built Real3D\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CReal3D::~CReal3D(void):
|
||||||
|
*
|
||||||
|
* Destructor.
|
||||||
|
*/
|
||||||
|
CReal3D::~CReal3D(void)
|
||||||
|
{
|
||||||
|
// Dump memory
|
||||||
|
#if 0
|
||||||
|
FILE *fp;
|
||||||
|
fp = fopen("8c000000", "wb");
|
||||||
|
if (NULL != fp)
|
||||||
|
{
|
||||||
|
fwrite(cullingRAMLo, sizeof(UINT8), 0x400000, fp);
|
||||||
|
fclose(fp);
|
||||||
|
printf("dumped %s\n", "8c000000");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
printf("unable to dump %s\n", "8c000000");
|
||||||
|
fp = fopen("8e000000", "wb");
|
||||||
|
if (NULL != fp)
|
||||||
|
{
|
||||||
|
fwrite(cullingRAMHi, sizeof(UINT8), 0x100000, fp);
|
||||||
|
fclose(fp);
|
||||||
|
printf("dumped %s\n", "8e000000");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
printf("unable to dump %s\n", "8e000000");
|
||||||
|
fp = fopen("98000000", "wb");
|
||||||
|
if (NULL != fp)
|
||||||
|
{
|
||||||
|
fwrite(polyRAM, sizeof(UINT8), 0x400000, fp);
|
||||||
|
fclose(fp);
|
||||||
|
printf("dumped %s\n", "98000000");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
printf("unable to dump %s\n", "98000000");
|
||||||
|
fp = fopen("texram", "wb");
|
||||||
|
if (NULL != fp)
|
||||||
|
{
|
||||||
|
fwrite(textureRAM, sizeof(UINT8), 0x800000, fp);
|
||||||
|
fclose(fp);
|
||||||
|
printf("dumped %s\n", "texram");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
printf("unable to dump %s\n", "texram");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Render3D = NULL;
|
||||||
|
if (memoryPool != NULL)
|
||||||
|
{
|
||||||
|
delete [] memoryPool;
|
||||||
|
memoryPool = NULL;
|
||||||
|
}
|
||||||
|
cullingRAMLo = NULL;
|
||||||
|
cullingRAMHi = NULL;
|
||||||
|
polyRAM = NULL;
|
||||||
|
textureRAM = NULL;
|
||||||
|
textureFIFO = NULL;
|
||||||
|
vrom = NULL;
|
||||||
|
DebugLog("Destroyed Real3D\n");
|
||||||
|
}
|
398
Model3/Real3D.h
Normal file
398
Model3/Real3D.h
Normal file
|
@ -0,0 +1,398 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Real3D.h
|
||||||
|
*
|
||||||
|
* Header file defining the CReal3D class: the Model 3's Real3D-based graphics
|
||||||
|
* hardware.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDED_REAL3D_H
|
||||||
|
#define INCLUDED_REAL3D_H
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CReal3D:
|
||||||
|
*
|
||||||
|
* Model 3 Real3D-based graphics hardware. This class manages the hardware
|
||||||
|
* state and drives the rendering process (scene database traversal). Actual
|
||||||
|
* rasterization and matrix transformations are carried out by the graphics
|
||||||
|
* engine.
|
||||||
|
*/
|
||||||
|
class CReal3D: public CPCIDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
* SaveState(SaveState):
|
||||||
|
*
|
||||||
|
* Saves an image of the current device state.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* SaveState Block file to save state information to.
|
||||||
|
*/
|
||||||
|
void SaveState(CBlockFile *SaveState);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LoadState(SaveState):
|
||||||
|
*
|
||||||
|
* Loads and a state image.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* SaveState Block file to load state information from.
|
||||||
|
*/
|
||||||
|
void LoadState(CBlockFile *SaveState);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RenderFrame(void):
|
||||||
|
*
|
||||||
|
* Traverses the scene database and renders a frame. Must be called after
|
||||||
|
* BeginFrame() but before EndFrame().
|
||||||
|
*/
|
||||||
|
void RenderFrame(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* BeginFrame(void):
|
||||||
|
*
|
||||||
|
* Prepare to render a new frame. Must be called once per frame prior to
|
||||||
|
* drawing anything.
|
||||||
|
*/
|
||||||
|
void BeginFrame(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* EndFrame(void):
|
||||||
|
*
|
||||||
|
* Signals the end of rendering for this frame. Must be called last during
|
||||||
|
* the frame.
|
||||||
|
*/
|
||||||
|
void EndFrame(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Flush(void):
|
||||||
|
*
|
||||||
|
* Triggers the beginning of a new frame. All textures in the texture FIFO
|
||||||
|
* are uploaded and the FIFO is reset. On the real device, this seems to
|
||||||
|
* cause a frame to be rendered as well but this is not performed here.
|
||||||
|
*
|
||||||
|
* This should be called when the command port is written.
|
||||||
|
*/
|
||||||
|
void Flush(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ReadDMARegister8(reg):
|
||||||
|
* ReadDMARegister32(reg):
|
||||||
|
*
|
||||||
|
* Reads from a DMA register. Multi-byte reads are returned as little
|
||||||
|
* endian and must be flipped if called by a big endian device.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* reg Register number to read from (0-0xFF only).
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Data of the requested size, in little endian.
|
||||||
|
*/
|
||||||
|
UINT8 ReadDMARegister8(unsigned reg);
|
||||||
|
UINT32 ReadDMARegister32(unsigned reg);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WriteDMARegister8(reg, data):
|
||||||
|
* WriteDMARegister32(reg, data);
|
||||||
|
*
|
||||||
|
* Write to a DMA register. Multi-byte writes by big endian devices must be
|
||||||
|
* byte reversed (this is a little endian device).
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* reg Register number to read from (0-0xFF only).
|
||||||
|
* data Data to write.
|
||||||
|
*/
|
||||||
|
void WriteDMARegister8(unsigned reg, UINT8 data);
|
||||||
|
void WriteDMARegister32(unsigned reg, UINT32 data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WriteLowCullingRAM(addr, data):
|
||||||
|
*
|
||||||
|
* Writes the low culling RAM region. Because this is a little endian
|
||||||
|
* device, big endian devices/buses have to take care to manually reverse
|
||||||
|
* the data before writing.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* addr Word (32-bit) aligned address ranging from 0 to 0x3FFFFC.
|
||||||
|
* User must ensure address is properly clamped.
|
||||||
|
* data Data to write.
|
||||||
|
*/
|
||||||
|
void WriteLowCullingRAM(UINT32 addr, UINT32 data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WriteHighCullingRAM(addr, data):
|
||||||
|
*
|
||||||
|
* Writes the high culling RAM region. Because this is a little endian
|
||||||
|
* device, big endian devices/buses have to take care to manually reverse
|
||||||
|
* the data before writing.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* addr Word (32-bit) aligned address ranging from 0 to 0xFFFFC.
|
||||||
|
* User must ensure address is properly clamped.
|
||||||
|
* data Data to write.
|
||||||
|
*/
|
||||||
|
void WriteHighCullingRAM(UINT32 addr, UINT32 data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WriteTextureFIFO(data):
|
||||||
|
*
|
||||||
|
* Writes to the 1MB texture FIFO. Because this is a little endian device,
|
||||||
|
* big endian devices/buses have to take care to manually reverse the data
|
||||||
|
* before writing.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* data Data to write.
|
||||||
|
*/
|
||||||
|
void WriteTextureFIFO(UINT32 data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WriteTexturePort(reg, data):
|
||||||
|
*
|
||||||
|
* Writes to the VROM texture ports. Register 0 is the word-granular VROM
|
||||||
|
* address of the texture, register 4 is the texture information header,
|
||||||
|
* and register 8 is the size of the texture in words.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* reg Register number: must be 0, 4, 8, 0xC, 0x10, or 0x14 only.
|
||||||
|
* data The 32-bit word to write to the register. A write to
|
||||||
|
* register 8 triggers the upload.
|
||||||
|
*/
|
||||||
|
void WriteTexturePort(unsigned reg, UINT32 data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WritePolygonRAM(addr, data):
|
||||||
|
*
|
||||||
|
* Writes the polygon RAM region. Because this is a little endian device,
|
||||||
|
* big endian devices/buses have to take care to manually reverse the data
|
||||||
|
* before writing.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* addr Word (32-bit) aligned address ranging from 0 to 0x3FFFFC.
|
||||||
|
* User must ensure address is properly clamped.
|
||||||
|
* data Data to write.
|
||||||
|
*/
|
||||||
|
void WritePolygonRAM(UINT32 addr, UINT32 data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ReadTAP(void):
|
||||||
|
*
|
||||||
|
* Reads the JTAG Test Access Port.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* The TDO bit (either 1 or 0).
|
||||||
|
*/
|
||||||
|
unsigned ReadTAP(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* void WriteTAP(tck, tms, tdi, trst):
|
||||||
|
*
|
||||||
|
* Writes to the JTAG TAP. State changes only occur on the rising edge of
|
||||||
|
* the clock (tck = 1). Each of the inputs is a single bit only and must be
|
||||||
|
* either 0 or 1, or the code will fail.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* tck Clock.
|
||||||
|
* tms Test mode select.
|
||||||
|
* tdi Serial data input. Must be 0 or 1 only!
|
||||||
|
* trst Reset.
|
||||||
|
*/
|
||||||
|
void WriteTAP(unsigned tck, unsigned tms, unsigned tdi, unsigned trst);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ReadRegister(reg):
|
||||||
|
*
|
||||||
|
* Reads one of the status registers.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* reg Register offset (32-bit aligned). From 0x00 to 0x3C.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* The 32-bit status register.
|
||||||
|
*/
|
||||||
|
UINT32 ReadRegister(unsigned reg);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ReadPCIConfigSpace(device, reg, bits, offset):
|
||||||
|
*
|
||||||
|
* Reads a PCI configuration space register. See CPCIDevice definition for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* device Device number (ignored, not needed).
|
||||||
|
* reg Register number.
|
||||||
|
* bits Bit width of access (8, 16, or 32 only).;
|
||||||
|
* offset Byte offset within register, aligned to the specified bit
|
||||||
|
* width, and offset from the 32-bit aligned base of the
|
||||||
|
* register number.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Register data.
|
||||||
|
*/
|
||||||
|
UINT32 ReadPCIConfigSpace(unsigned device, unsigned reg, unsigned bits, unsigned width);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WritePCIConfigSpace(device, reg, bits, offset, data):
|
||||||
|
*
|
||||||
|
* Writes to a PCI configuration space register. See CPCIDevice definition
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* device Device number (ignored, not needed).
|
||||||
|
* reg Register number.
|
||||||
|
* bits Bit width of access (8, 16, or 32 only).
|
||||||
|
* offset Byte offset within register, aligned to the specified bit
|
||||||
|
* width, and offset from the 32-bit aligned base of the
|
||||||
|
* register number.
|
||||||
|
* data Data.
|
||||||
|
*/
|
||||||
|
void WritePCIConfigSpace(unsigned device, unsigned reg, unsigned bits, unsigned width, UINT32 data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset(void):
|
||||||
|
*
|
||||||
|
* Resets the Real3D device. Must be called before reading/writing the
|
||||||
|
* device.
|
||||||
|
*/
|
||||||
|
void Reset(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AttachRenderer(render2DPtr):
|
||||||
|
*
|
||||||
|
* Attaches a 3D renderer for the Real3D to use. This function will
|
||||||
|
* immediately pass along the information that a CRender3D object needs to
|
||||||
|
* work with.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* Render3DPtr Pointer to a 3D renderer object.
|
||||||
|
*/
|
||||||
|
void AttachRenderer(CRender3D *Render3DPtr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SetStep(stepID):
|
||||||
|
*
|
||||||
|
* Sets the Model 3 hardware stepping, which also determines the Real3D
|
||||||
|
* functionality. The default is Step 1.0. This should be called prior to
|
||||||
|
* any other emulation functions and after Init().
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* stepID 0x10 for Step 1.0, 0x15 for Step 1.5, 0x20 for Step 2.0,
|
||||||
|
* or 0x21 for Step 2.1. Anything else defaults to 1.0.
|
||||||
|
*/
|
||||||
|
void SetStep(int stepID);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Init(vromPtr, BusObjectPtr, IRQObjectPtr, dmaIRQBit):
|
||||||
|
*
|
||||||
|
* One-time initialization of the context. Must be called prior to all
|
||||||
|
* other members. Connects the Real3D device to its video ROM and allocates
|
||||||
|
* memory for RAM regions.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* vromPtr A pointer to video ROM (with each 32-bit word in
|
||||||
|
* its native little endian format).
|
||||||
|
* BusObjectPtr Pointer to the bus that the 53C810 has control
|
||||||
|
* over. Used to read/write memory.
|
||||||
|
* IRQObjectPtr Pointer to the IRQ controller. Used to trigger SCSI
|
||||||
|
* and DMA interrupts.
|
||||||
|
* dmaIRQBit IRQ identifier bit to pass along to IRQ controller
|
||||||
|
* when asserting interrupts.
|
||||||
|
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* OKAY if successful otherwise FAIL (not enough memory). Prints own
|
||||||
|
* errors.
|
||||||
|
*/
|
||||||
|
BOOL Init(const UINT8 *vromPtr, CBus *BusObjectPtr, CIRQ *IRQObjectPtr, unsigned dmaIRQBit);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CReal3D(void):
|
||||||
|
* ~CReal3D(void):
|
||||||
|
*
|
||||||
|
* Constructor and destructor.
|
||||||
|
*/
|
||||||
|
CReal3D(void);
|
||||||
|
~CReal3D(void);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Private member functions
|
||||||
|
void DMACopy(void);
|
||||||
|
void InsertBit(UINT8 *buf, unsigned bitNum, unsigned bit);
|
||||||
|
void InsertID(UINT32 id, unsigned startBit);
|
||||||
|
unsigned Shift(UINT8 *data, unsigned numBits);
|
||||||
|
void StoreTexture(unsigned xPos, unsigned yPos, unsigned width, unsigned height, UINT16 *texData, unsigned bytesPerTexel);
|
||||||
|
void UploadTexture(UINT32 header, UINT16 *texData);
|
||||||
|
|
||||||
|
// Renderer attached to the Real3D
|
||||||
|
CRender3D *Render3D;
|
||||||
|
|
||||||
|
// Data passed from Model 3 object
|
||||||
|
const UINT32 *vrom; // Video ROM
|
||||||
|
int step; // hardware stepping (as in GameInfo structure)
|
||||||
|
UINT32 pciID; // PCI vendor and device ID
|
||||||
|
|
||||||
|
// Real3D memory
|
||||||
|
UINT8 *memoryPool; // all memory allocated here
|
||||||
|
UINT32 *cullingRAMLo; // 4MB of culling RAM at 8C000000
|
||||||
|
UINT32 *cullingRAMHi; // 1MB of culling RAM at 8E000000
|
||||||
|
UINT32 *polyRAM; // 4MB of polygon RAM at 98000000
|
||||||
|
UINT16 *textureRAM; // 8MB of internal texture RAM
|
||||||
|
UINT32 *textureFIFO; // 1MB texture FIFO at 0x94000000
|
||||||
|
unsigned fifoIdx; // index into texture FIFO
|
||||||
|
UINT32 vromTextureAddr; // VROM texture port address data
|
||||||
|
UINT32 vromTextureHeader; // VROM texture port header data
|
||||||
|
|
||||||
|
// Big endian bus object for DMA memory access
|
||||||
|
CBus *Bus;
|
||||||
|
|
||||||
|
// IRQ handling
|
||||||
|
CIRQ *IRQ; // IRQ controller
|
||||||
|
unsigned dmaIRQ; // IRQ bit to use when calling IRQ handler
|
||||||
|
|
||||||
|
// DMA device
|
||||||
|
UINT32 dmaSrc;
|
||||||
|
UINT32 dmaDest;
|
||||||
|
UINT32 dmaLength;
|
||||||
|
UINT32 dmaData;
|
||||||
|
UINT32 dmaUnknownReg;
|
||||||
|
UINT8 dmaStatus;
|
||||||
|
UINT8 dmaConfig;
|
||||||
|
|
||||||
|
// Command port
|
||||||
|
BOOL commandPortWritten;
|
||||||
|
|
||||||
|
// Status and command registers
|
||||||
|
UINT32 status;
|
||||||
|
|
||||||
|
// JTAG Test Access Port
|
||||||
|
UINT64 tapCurrentInstruction; // latched IR (not always equal to IR)
|
||||||
|
UINT64 tapIR; // instruction register (46 bits)
|
||||||
|
UINT8 tapID[32]; // ASIC ID code data buffer
|
||||||
|
unsigned tapIDSize; // size of ID data in bits
|
||||||
|
unsigned tapTDO; // bit shifted out to TDO
|
||||||
|
int tapState; // current state
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // INCLUDED_REAL3D_H
|
379
Model3/SoundBoard.cpp
Normal file
379
Model3/SoundBoard.cpp
Normal file
|
@ -0,0 +1,379 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SoundBoard.cpp
|
||||||
|
*
|
||||||
|
* Model 3 sound board. Implementation of the CSoundBoard class. This class can
|
||||||
|
* only be instantiated once because it relies on global variables (the non-OOP
|
||||||
|
* 68K core).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Supermodel.h"
|
||||||
|
|
||||||
|
//TEMP: these need to be dynamically allocated in the memory pool
|
||||||
|
static INT16 leftBuffer[44100/60],rightBuffer[44100/60];
|
||||||
|
static FILE *soundFP;
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Global 68K Access Handlers
|
||||||
|
|
||||||
|
The 68K must interface with globally-accessible memory maps and access
|
||||||
|
handlers. Turbo68K uses the __cdecl calling convention. Note that this is not
|
||||||
|
explicitly defined for the Turbo68K functions themselves but in case Turbo68K
|
||||||
|
crashes, this may be the culprit.
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
// Prototypes for access handlers
|
||||||
|
static UINT8 __cdecl UnknownRead8(UINT32);
|
||||||
|
static UINT16 __cdecl UnknownRead16(UINT32);
|
||||||
|
static UINT32 __cdecl UnknownRead32(UINT32);
|
||||||
|
static void __cdecl UnknownWrite8(UINT32, UINT8);
|
||||||
|
static void __cdecl UnknownWrite16(UINT32, UINT16);
|
||||||
|
static void __cdecl UnknownWrite32(UINT32, UINT32);
|
||||||
|
|
||||||
|
// Memory maps for 68K
|
||||||
|
#ifdef SUPERMODEL_SOUND
|
||||||
|
struct TURBO68K_FETCHREGION mapFetch[] =
|
||||||
|
{
|
||||||
|
{ 0x000000, 0x0FFFFF, NULL }, // SCSP1 RAM
|
||||||
|
{ 0x200000, 0x2FFFFF, NULL }, // SCSP2 RAM
|
||||||
|
{ 0x600000, 0x67FFFF, NULL }, // program ROM
|
||||||
|
{ -1, -1, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TURBO68K_DATAREGION mapRead8[] =
|
||||||
|
{
|
||||||
|
{ 0x000000, 0x0FFFFF, NULL, NULL }, // SCSP1 RAM
|
||||||
|
{ 0x200000, 0x2FFFFF, NULL, NULL }, // SCSP2 RAM
|
||||||
|
{ 0x600000, 0x67FFFF, NULL, NULL }, // program ROM
|
||||||
|
{ 0x800000, 0x9FFFFF, NULL, NULL }, // sample ROM (low 2 MB)
|
||||||
|
{ 0xA00000, 0xDFFFFF, NULL, NULL }, // sample ROM (bank)
|
||||||
|
{ 0xE00000, 0xFFFFFF, NULL, NULL }, // sample ROM (bank)
|
||||||
|
{ 0x100000, 0x10FFFF, NULL, SCSP_Master_r8 },
|
||||||
|
{ 0x300000, 0x30FFFF, NULL, SCSP_Slave_r8 },
|
||||||
|
{ 0x000000, 0xFFFFFF, NULL, UnknownRead8 },
|
||||||
|
{ -1, -1, NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TURBO68K_DATAREGION mapRead16[] =
|
||||||
|
{
|
||||||
|
{ 0x000000, 0x0FFFFF, NULL, NULL }, // SCSP1 RAM
|
||||||
|
{ 0x200000, 0x2FFFFF, NULL, NULL }, // SCSP2 RAM
|
||||||
|
{ 0x600000, 0x67FFFF, NULL, NULL }, // program ROM
|
||||||
|
{ 0x800000, 0x9FFFFF, NULL, NULL }, // sample ROM (low 2 MB)
|
||||||
|
{ 0xA00000, 0xDFFFFF, NULL, NULL }, // sample ROM (bank)
|
||||||
|
{ 0xE00000, 0xFFFFFF, NULL, NULL }, // sample ROM (bank)
|
||||||
|
{ 0x100000, 0x10FFFF, NULL, SCSP_Master_r16 },
|
||||||
|
{ 0x300000, 0x30FFFF, NULL, SCSP_Slave_r16 },
|
||||||
|
{ 0x000000, 0xFFFFFF, NULL, UnknownRead16 },
|
||||||
|
{ -1, -1, NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TURBO68K_DATAREGION mapRead32[] =
|
||||||
|
{
|
||||||
|
{ 0x000000, 0x0FFFFF, NULL, NULL }, // SCSP1 RAM
|
||||||
|
{ 0x200000, 0x2FFFFF, NULL, NULL }, // SCSP2 RAM
|
||||||
|
{ 0x600000, 0x67FFFF, NULL, NULL }, // program ROM
|
||||||
|
{ 0x800000, 0x9FFFFF, NULL, NULL }, // sample ROM (low 2 MB)
|
||||||
|
{ 0xA00000, 0xDFFFFF, NULL, NULL }, // sample ROM (bank)
|
||||||
|
{ 0xE00000, 0xFFFFFF, NULL, NULL }, // sample ROM (bank)
|
||||||
|
{ 0x100000, 0x10FFFF, NULL, SCSP_Master_r32 },
|
||||||
|
{ 0x300000, 0x30FFFF, NULL, SCSP_Slave_r32 },
|
||||||
|
{ 0x000000, 0xFFFFFF, NULL, UnknownRead32 },
|
||||||
|
{ -1, -1, NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TURBO68K_DATAREGION mapWrite8[] =
|
||||||
|
{
|
||||||
|
{ 0x000000, 0x0FFFFF, NULL, NULL }, // SCSP1 RAM
|
||||||
|
{ 0x200000, 0x2FFFFF, NULL, NULL }, // SCSP2 RAM
|
||||||
|
{ 0x100000, 0x10FFFF, NULL, SCSP_Master_w8 },
|
||||||
|
{ 0x300000, 0x30FFFF, NULL, SCSP_Slave_w8 },
|
||||||
|
{ 0x000000, 0xFFFFFF, NULL, UnknownWrite8 },
|
||||||
|
{ -1, -1, NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TURBO68K_DATAREGION mapWrite16[] =
|
||||||
|
{
|
||||||
|
{ 0x000000, 0x0FFFFF, NULL, NULL }, // SCSP1 RAM
|
||||||
|
{ 0x200000, 0x2FFFFF, NULL, NULL }, // SCSP2 RAM
|
||||||
|
{ 0x100000, 0x10FFFF, NULL, SCSP_Master_w16 },
|
||||||
|
{ 0x300000, 0x30FFFF, NULL, SCSP_Slave_w16 },
|
||||||
|
{ 0x000000, 0xFFFFFF, NULL, UnknownWrite16 },
|
||||||
|
{ -1, -1, NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TURBO68K_DATAREGION mapWrite32[] =
|
||||||
|
{
|
||||||
|
{ 0x000000, 0x0FFFFF, NULL, NULL }, // SCSP1 RAM
|
||||||
|
{ 0x200000, 0x2FFFFF, NULL, NULL }, // SCSP2 RAM
|
||||||
|
{ 0x100000, 0x10FFFF, NULL, SCSP_Master_w32 },
|
||||||
|
{ 0x300000, 0x30FFFF, NULL, SCSP_Slave_w32 },
|
||||||
|
{ 0x000000, 0xFFFFFF, NULL, UnknownWrite32 },
|
||||||
|
{ -1, -1, NULL, NULL }
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static UINT8 __cdecl UnknownRead8(UINT32 addr)
|
||||||
|
{
|
||||||
|
printf("68K read from %06X (byte)\n", addr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UINT16 __cdecl UnknownRead16(UINT32 addr)
|
||||||
|
{
|
||||||
|
printf("68K read from %06X (word)\n", addr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UINT32 __cdecl UnknownRead32(UINT32 addr)
|
||||||
|
{
|
||||||
|
printf("68K read from %06X (longword)\n", addr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void __cdecl UnknownWrite8(UINT32 addr, UINT8 data)
|
||||||
|
{
|
||||||
|
printf("68K wrote %06X=%02X\n", addr, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __cdecl UnknownWrite16(UINT32 addr, UINT16 data)
|
||||||
|
{
|
||||||
|
printf("68K wrote %06X=%04X\n", addr, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __cdecl UnknownWrite32(UINT32 addr, UINT32 data)
|
||||||
|
{
|
||||||
|
printf("68K wrote %06X=%08X\n", addr, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Emulation Functions
|
||||||
|
******************************************************************************/
|
||||||
|
int irqLine = 0;
|
||||||
|
|
||||||
|
// SCSP callback for generating IRQs
|
||||||
|
void SCSP68KIRQCallback(int irqNum)
|
||||||
|
{
|
||||||
|
#ifdef SUPERMODEL_SOUND
|
||||||
|
//printf("IRQ: %d\n", irqNum);
|
||||||
|
irqLine = irqNum;
|
||||||
|
//Turbo68KInterrupt(irqNum, TURBO68K_AUTOVECTOR);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// SCSP callback for running the 68K
|
||||||
|
int SCSP68KRunCallback(int numCycles)
|
||||||
|
{
|
||||||
|
#ifdef SUPERMODEL_SOUND
|
||||||
|
for (int i = 0; i < numCycles; i++)
|
||||||
|
{
|
||||||
|
if (irqLine)
|
||||||
|
Turbo68KInterrupt(irqLine,TURBO68K_AUTOVECTOR);
|
||||||
|
Turbo68KRun(1);
|
||||||
|
}
|
||||||
|
return numCycles;
|
||||||
|
//Turbo68KRun(numCycles);
|
||||||
|
//return Turbo68KGetElapsedCycles();
|
||||||
|
#else
|
||||||
|
return numCycles;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSoundBoard::WriteMIDIPort(UINT8 data)
|
||||||
|
{
|
||||||
|
SCSP_MidiIn(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSoundBoard::RunFrame(void)
|
||||||
|
{
|
||||||
|
#ifdef SUPERMODEL_SOUND
|
||||||
|
SCSP_Update();
|
||||||
|
|
||||||
|
// Output to binary file
|
||||||
|
INT16 s;
|
||||||
|
for (int i = 0; i < 44100/60; i++)
|
||||||
|
{
|
||||||
|
s = ((UINT16)leftBuffer[i]>>8) | ((leftBuffer[i]&0xFF)<<8);
|
||||||
|
fwrite(&s, sizeof(INT16), 1, soundFP); // left channel
|
||||||
|
s = ((UINT16)rightBuffer[i]>>8) | ((rightBuffer[i]&0xFF)<<8);
|
||||||
|
fwrite(&s, sizeof(INT16), 1, soundFP); // right channel
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSoundBoard::Reset(void)
|
||||||
|
{
|
||||||
|
#ifdef SUPERMODEL_SOUND
|
||||||
|
memcpy(ram1, soundROM, 16); // copy 68K vector table
|
||||||
|
Turbo68KReset();
|
||||||
|
/*
|
||||||
|
printf("68K PC=%06X\n", Turbo68KReadPC());
|
||||||
|
for (int i = 0; i < 1000000; i++)
|
||||||
|
{
|
||||||
|
//printf("%06X\n", Turbo68KReadPC());
|
||||||
|
Turbo68KRun(1);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
#endif
|
||||||
|
DebugLog("Sound Board Reset\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Configuration, Initialization, and Shutdown
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
// Offsets of memory regions within sound board's pool
|
||||||
|
#define OFFSET_RAM1 0 // 1 MB SCSP1 RAM
|
||||||
|
#define OFFSET_RAM2 0x100000 // 1 MB SCSP2 RAM
|
||||||
|
#define MEMORY_POOL_SIZE (0x100000+0x100000)
|
||||||
|
|
||||||
|
BOOL CSoundBoard::Init(const UINT8 *soundROMPtr, const UINT8 *sampleROMPtr, CIRQ *ppcIRQObjectPtr, unsigned soundIRQBit)
|
||||||
|
{
|
||||||
|
float memSizeMB = (float)MEMORY_POOL_SIZE/(float)0x100000;
|
||||||
|
|
||||||
|
// Attach IRQ controller
|
||||||
|
ppcIRQ = ppcIRQObjectPtr;
|
||||||
|
ppcSoundIRQBit = soundIRQBit;
|
||||||
|
|
||||||
|
// Receive sound ROMs
|
||||||
|
soundROM = soundROMPtr;
|
||||||
|
sampleROM = sampleROMPtr;
|
||||||
|
|
||||||
|
// Allocate all memory for RAM
|
||||||
|
memoryPool = new(std::nothrow) UINT8[MEMORY_POOL_SIZE];
|
||||||
|
if (NULL == memoryPool)
|
||||||
|
return ErrorLog("Insufficient memory for sound board (needs %1.1f MB).", memSizeMB);
|
||||||
|
memset(memoryPool, 0, MEMORY_POOL_SIZE);
|
||||||
|
|
||||||
|
// Set up memory pointers
|
||||||
|
ram1 = &memoryPool[OFFSET_RAM1];
|
||||||
|
ram2 = &memoryPool[OFFSET_RAM2];
|
||||||
|
|
||||||
|
// Initialize 68K core
|
||||||
|
#ifdef SUPERMODEL_SOUND
|
||||||
|
mapFetch[0].ptr = mapRead8[0].ptr = mapRead16[0].ptr = mapRead32[0].ptr =
|
||||||
|
mapWrite8[0].ptr = mapWrite16[0].ptr = mapWrite32[0].ptr = (UINT32)ram1 - mapFetch[0].base;;
|
||||||
|
|
||||||
|
mapFetch[1].ptr = mapRead8[1].ptr = mapRead16[1].ptr = mapRead32[1].ptr =
|
||||||
|
mapWrite8[1].ptr = mapWrite16[1].ptr = mapWrite32[1].ptr = (UINT32)ram2 - mapFetch[1].base;
|
||||||
|
|
||||||
|
mapFetch[2].ptr = mapRead8[2].ptr = mapRead16[2].ptr = mapRead32[2].ptr = (UINT32)soundROM - mapFetch[2].base;
|
||||||
|
|
||||||
|
mapRead8[3].ptr = mapRead16[3].ptr = mapRead32[3].ptr = (UINT32)&sampleROM[0x000000] - mapRead8[3].base;
|
||||||
|
|
||||||
|
mapRead8[4].ptr = mapRead16[4].ptr = mapRead32[4].ptr = (UINT32)&sampleROM[0x200000] - mapRead8[4].base;
|
||||||
|
|
||||||
|
mapRead8[5].ptr = mapRead16[5].ptr = mapRead32[5].ptr = (UINT32)&sampleROM[0x600000] - mapRead8[5].base;
|
||||||
|
|
||||||
|
Turbo68KInit();
|
||||||
|
Turbo68KSetFetch(mapFetch, NULL);
|
||||||
|
Turbo68KSetReadByte(mapRead8, NULL);
|
||||||
|
Turbo68KSetReadWord(mapRead16, NULL);
|
||||||
|
Turbo68KSetReadLong(mapRead32, NULL);
|
||||||
|
Turbo68KSetWriteByte(mapWrite8, NULL);
|
||||||
|
Turbo68KSetWriteWord(mapWrite16, NULL);
|
||||||
|
Turbo68KSetWriteLong(mapWrite32, NULL);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Initialize SCSPs
|
||||||
|
SCSP_SetBuffers(leftBuffer, rightBuffer, 44100/60);
|
||||||
|
SCSP_SetCB(SCSP68KRunCallback, SCSP68KIRQCallback, ppcIRQ, ppcSoundIRQBit);
|
||||||
|
SCSP_Init(2);
|
||||||
|
SCSP_SetRAM(0, ram1);
|
||||||
|
SCSP_SetRAM(1, ram2);
|
||||||
|
|
||||||
|
// Binary logging
|
||||||
|
#ifdef SUPERMODEL_SOUND
|
||||||
|
soundFP = fopen("sound.bin","wb"); // delete existing file
|
||||||
|
fclose(soundFP);
|
||||||
|
soundFP = fopen("sound.bin","ab"); // append mode
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return OKAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSoundBoard::CSoundBoard(void)
|
||||||
|
{
|
||||||
|
memoryPool = NULL;
|
||||||
|
ram1 = NULL;
|
||||||
|
ram2 = NULL;
|
||||||
|
|
||||||
|
DebugLog("Built Sound Board\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Reverse16(UINT8 *buf, unsigned size)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
UINT8 tmp;
|
||||||
|
|
||||||
|
for (i = 0; i < size; i += 2)
|
||||||
|
{
|
||||||
|
tmp = buf[i+0];
|
||||||
|
buf[i+0] = buf[i+1];
|
||||||
|
buf[i+1] = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CSoundBoard::~CSoundBoard(void)
|
||||||
|
{
|
||||||
|
#ifdef SUPERMODEL_SOUND
|
||||||
|
// close binary log file
|
||||||
|
fclose(soundFP);
|
||||||
|
//#if 0
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
Reverse16(ram1, 0x100000);
|
||||||
|
Reverse16(ram2, 0x100000);
|
||||||
|
fp = fopen("scspRAM1", "wb");
|
||||||
|
if (NULL != fp)
|
||||||
|
{
|
||||||
|
fwrite(ram1, sizeof(UINT8), 0x100000, fp);
|
||||||
|
fclose(fp);
|
||||||
|
printf("dumped %s\n", "scspRAM1");
|
||||||
|
|
||||||
|
}
|
||||||
|
fp = fopen("scspRAM2", "wb");
|
||||||
|
if (NULL != fp)
|
||||||
|
{
|
||||||
|
fwrite(ram2, sizeof(UINT8), 0x100000, fp);
|
||||||
|
fclose(fp);
|
||||||
|
printf("dumped %s\n", "scspRAM2");
|
||||||
|
|
||||||
|
}
|
||||||
|
//#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SCSP_Deinit();
|
||||||
|
|
||||||
|
if (memoryPool != NULL)
|
||||||
|
{
|
||||||
|
delete [] memoryPool;
|
||||||
|
memoryPool = NULL;
|
||||||
|
}
|
||||||
|
ram1 = NULL;
|
||||||
|
ram2 = NULL;
|
||||||
|
DebugLog("Destroyed Sound Board\n");
|
||||||
|
}
|
103
Model3/SoundBoard.h
Normal file
103
Model3/SoundBoard.h
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SoundBoard.h
|
||||||
|
*
|
||||||
|
* Header file defining the CSoundBoard class: Model 3 audio subsystem.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDED_SOUNDBOARD_H
|
||||||
|
#define INCLUDED_SOUNDBOARD_H
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CSoundBoard:
|
||||||
|
*
|
||||||
|
* Model 3 sound board (68K CPU + 2 x SCSP).
|
||||||
|
*/
|
||||||
|
class CSoundBoard
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
* WriteMIDIPort(data):
|
||||||
|
*
|
||||||
|
* Writes to the sound board MIDI port.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* data Byte to write to MIDI port.
|
||||||
|
*/
|
||||||
|
void WriteMIDIPort(UINT8 data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RunFrame(void):
|
||||||
|
*
|
||||||
|
* Runs the sound board for one frame, updating sound in the process.
|
||||||
|
*/
|
||||||
|
void RunFrame(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset(void):
|
||||||
|
*
|
||||||
|
* Resets the sound board.
|
||||||
|
*/
|
||||||
|
void Reset(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Init(soundROMPtr, sampleROMPtr):
|
||||||
|
*
|
||||||
|
* One-time initialization. Must be called prior to all other members.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* soundROMPtr Pointer to sound ROM (68K program).
|
||||||
|
* sampleROMPtr Pointer to sample ROM.
|
||||||
|
* ppcIRQObjectPtr Pointer to PowerPC-side IRQ object.
|
||||||
|
* soundIRQBit IRQ bit mask to use for sound board PowerPC IRQs.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* OKAY if successful, FAIL if unable to allocate memory. Prints own
|
||||||
|
* error messages.
|
||||||
|
*/
|
||||||
|
BOOL Init(const UINT8 *soundROMPtr, const UINT8 *sampleROMPtr, CIRQ *ppcIRQObjectPtr, unsigned soundIRQBit);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CSoundBoard(void):
|
||||||
|
* ~CSoundBoard(void):
|
||||||
|
*
|
||||||
|
* Constructor and destructor.
|
||||||
|
*/
|
||||||
|
CSoundBoard(void);
|
||||||
|
~CSoundBoard(void);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// PowerPC IRQ controller
|
||||||
|
CIRQ *ppcIRQ;
|
||||||
|
unsigned ppcSoundIRQBit;
|
||||||
|
|
||||||
|
// Sound board memory
|
||||||
|
const UINT8 *soundROM; // 68K program ROM (passed in from parent object)
|
||||||
|
const UINT8 *sampleROM; // 68K sample ROM (passed in from parent object)
|
||||||
|
UINT8 *memoryPool; // single allocated region for all sound board RAM
|
||||||
|
UINT8 *ram1, *ram2; // SCSP1 and SCSP2 RAM
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // INCLUDED_SOUNDBOARD_H
|
205
Model3/TileGen.cpp
Normal file
205
Model3/TileGen.cpp
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TileGen.cpp
|
||||||
|
*
|
||||||
|
* Implementation of the CTileGen class: 2D tile generator.
|
||||||
|
*
|
||||||
|
* TO-DO List:
|
||||||
|
* -----------
|
||||||
|
* - For consistency, the registers should probably be byte reversed (this is a
|
||||||
|
* little endian device), forcing the Model3 Read32/Write32 handlers to
|
||||||
|
* manually reverse the data. This keeps with the convention for VRAM.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "Supermodel.h"
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Save States
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void CTileGen::SaveState(CBlockFile *SaveState)
|
||||||
|
{
|
||||||
|
SaveState->NewBlock("Tile Generator", __FILE__);
|
||||||
|
SaveState->Write(memoryPool, 0x100000+0x20000);
|
||||||
|
SaveState->Write(regs, sizeof(regs));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTileGen::LoadState(CBlockFile *SaveState)
|
||||||
|
{
|
||||||
|
if (OKAY != SaveState->FindBlock("Tile Generator"))
|
||||||
|
{
|
||||||
|
ErrorLog("Unable to load tile generator state. Save state file is corrupted.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load memory one word at a time
|
||||||
|
for (int i = 0; i < (0x100000+0x20000); i += 4)
|
||||||
|
{
|
||||||
|
UINT32 data;
|
||||||
|
|
||||||
|
SaveState->Read(&data, sizeof(data));
|
||||||
|
Render2D->WriteVRAM(i, data);
|
||||||
|
*(UINT32 *) &memoryPool[i] = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveState->Read(regs, sizeof(regs));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Rendering
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void CTileGen::BeginFrame(void)
|
||||||
|
{
|
||||||
|
Render2D->BeginFrame();
|
||||||
|
/*
|
||||||
|
printf("08: %X\n", regs[0x08/4]);
|
||||||
|
printf("0C: %X\n", regs[0x0C/4]);
|
||||||
|
printf("20: %X\n", regs[0x20/4]);
|
||||||
|
printf("40: %X\n", regs[0x40/4]);
|
||||||
|
printf("44: %X\n", regs[0x44/4]);
|
||||||
|
printf("60: %08X\n", regs[0x60/4]);
|
||||||
|
printf("64: %08X\n", regs[0x64/4]);
|
||||||
|
printf("68: %08X\n", regs[0x68/4]);
|
||||||
|
printf("6C: %08X\n", regs[0x6C/4]);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTileGen::EndFrame(void)
|
||||||
|
{
|
||||||
|
Render2D->EndFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Emulation Functions
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
UINT32 CTileGen::ReadRAM(unsigned addr)
|
||||||
|
{
|
||||||
|
return *(UINT32 *) &memoryPool[addr];
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTileGen::WriteRAM(unsigned addr, UINT32 data)
|
||||||
|
{
|
||||||
|
Render2D->WriteVRAM(addr,data); // inform renderer of update first
|
||||||
|
*(UINT32 *) &memoryPool[addr] = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTileGen::WriteRegister(unsigned reg, UINT32 data)
|
||||||
|
{
|
||||||
|
reg &= 0xFF;
|
||||||
|
regs[reg/4] = data;
|
||||||
|
|
||||||
|
switch (reg)
|
||||||
|
{
|
||||||
|
case 0x10: // IRQ acknowledge
|
||||||
|
IRQ->Deassert(data&0xFF);
|
||||||
|
break;
|
||||||
|
case 0x60:
|
||||||
|
break;
|
||||||
|
case 0x64:
|
||||||
|
break;
|
||||||
|
case 0x68:
|
||||||
|
break;
|
||||||
|
case 0x6C:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DebugLog("Tile Generator reg %02X = %08X\n", reg, data);
|
||||||
|
//printf("%02X = %08X\n", reg, data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTileGen::Reset(void)
|
||||||
|
{
|
||||||
|
memset(regs, 0, sizeof(regs));
|
||||||
|
memset(memoryPool, 0, 0x120000);
|
||||||
|
DebugLog("Tile Generator reset\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Configuration, Initialization, and Shutdown
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
void CTileGen::AttachRenderer(CRender2D *Render2DPtr)
|
||||||
|
{
|
||||||
|
Render2D = Render2DPtr;
|
||||||
|
Render2D->AttachVRAM(memoryPool);
|
||||||
|
Render2D->AttachRegisters(regs);
|
||||||
|
DebugLog("Tile Generator attached a Render2D object\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MEMORY_POOL_SIZE 0x120000
|
||||||
|
|
||||||
|
BOOL CTileGen::Init(CIRQ *IRQObjectPtr)
|
||||||
|
{
|
||||||
|
float memSizeMB = (float)MEMORY_POOL_SIZE/(float)0x100000;
|
||||||
|
|
||||||
|
// Allocate all memory for ROMs and PPC RAM
|
||||||
|
memoryPool = new(std::nothrow) UINT8[MEMORY_POOL_SIZE];
|
||||||
|
if (NULL == memoryPool)
|
||||||
|
return ErrorLog("Insufficient memory for tile generator object (needs %1.1f MB).", memSizeMB);
|
||||||
|
|
||||||
|
// Hook up the IRQ controller
|
||||||
|
IRQ = IRQObjectPtr;
|
||||||
|
|
||||||
|
DebugLog("Initialized Tile Generator (allocated %1.1f MB and connected to IRQ controller)\n", memSizeMB);
|
||||||
|
return OKAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
CTileGen::CTileGen(void)
|
||||||
|
{
|
||||||
|
IRQ = NULL;
|
||||||
|
memoryPool = NULL;
|
||||||
|
DebugLog("Built Tile Generator\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
CTileGen::~CTileGen(void)
|
||||||
|
{
|
||||||
|
// Dump tile generator RAM
|
||||||
|
#if 0
|
||||||
|
FILE *fp;
|
||||||
|
fp = fopen("tileram", "wb");
|
||||||
|
if (NULL != fp)
|
||||||
|
{
|
||||||
|
fwrite(memoryPool, sizeof(UINT8), 0x120000, fp);
|
||||||
|
fclose(fp);
|
||||||
|
printf("dumped %s\n", "tileram");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
printf("unable to dump %s\n", "tileram");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
IRQ = NULL;
|
||||||
|
if (memoryPool != NULL)
|
||||||
|
{
|
||||||
|
delete [] memoryPool;
|
||||||
|
memoryPool = NULL;
|
||||||
|
}
|
||||||
|
DebugLog("Destroyed Tile Generator\n");
|
||||||
|
}
|
177
Model3/TileGen.h
Normal file
177
Model3/TileGen.h
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TileGen.h
|
||||||
|
*
|
||||||
|
* Header file defining the CTileGen (tile generator device) class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDED_TILEGEN_H
|
||||||
|
#define INCLUDED_TILEGEN_H
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CTileGen:
|
||||||
|
*
|
||||||
|
* Tile generator device. The Model 3's tile generator handles not only the 2D
|
||||||
|
* tile map layers but also seems to control video output and VBL IRQs.
|
||||||
|
*/
|
||||||
|
class CTileGen
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
* SaveState(SaveState):
|
||||||
|
*
|
||||||
|
* Saves an image of the current device state.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* SaveState Block file to save state information to.
|
||||||
|
*/
|
||||||
|
void SaveState(CBlockFile *SaveState);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LoadState(SaveState):
|
||||||
|
*
|
||||||
|
* Loads and a state image.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* SaveState Block file to load state information from.
|
||||||
|
*/
|
||||||
|
void LoadState(CBlockFile *SaveState);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* BeginFrame(void):
|
||||||
|
*
|
||||||
|
* Prepare to render a new frame. Must be called once per frame prior to
|
||||||
|
* drawing anything.
|
||||||
|
*/
|
||||||
|
void BeginFrame(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* EndFrame(void):
|
||||||
|
*
|
||||||
|
* Signals the end of rendering for this frame. Must be called last during
|
||||||
|
* the frame.
|
||||||
|
*/
|
||||||
|
void EndFrame(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ReadRAM(addr):
|
||||||
|
*
|
||||||
|
* Reads the tile generator's little endian RAM (the word is returned with
|
||||||
|
* the MSB read from addr+3). If a big endian device is reading (PowerPC),
|
||||||
|
* the result must be manually flipped.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* addr Address in tile generator RAM. Caller must ensure it is
|
||||||
|
* clamped to the range 0x000000 to 0x11FFFF because this
|
||||||
|
* function does not.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* A 32-bit word read as little endian from the RAM address.
|
||||||
|
*/
|
||||||
|
UINT32 ReadRAM(unsigned addr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WriteRAM(addr, data):
|
||||||
|
*
|
||||||
|
* Writes to the tile generator's little endian RAM (the word's MSB is
|
||||||
|
* written to addr+3). If a big endian device is writing, the word must be
|
||||||
|
* flipped manually before passing it to this function.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* addr Address in tile generator RAM. Caller must ensure it is
|
||||||
|
* clamped to the range 0x000000 to 0x11FFFF because this
|
||||||
|
* function does not.
|
||||||
|
* data The data to write.
|
||||||
|
*/
|
||||||
|
void WriteRAM(unsigned addr, UINT32 data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WriteRegister(reg, data):
|
||||||
|
*
|
||||||
|
* Writes 32 bits of data to a (little endian) register. If a big endian
|
||||||
|
* device is writing, th word must be flipped.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* reg Aligned (32 bits) register offset (0x00-0xFC).
|
||||||
|
* data Data to write.
|
||||||
|
*/
|
||||||
|
void WriteRegister(unsigned reg, UINT32 data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset(void):
|
||||||
|
*
|
||||||
|
* Resets the device.
|
||||||
|
*/
|
||||||
|
void Reset(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AttachRenderer(render2DPtr):
|
||||||
|
*
|
||||||
|
* Attaches a 2D renderer for the tile generator to use. This function will
|
||||||
|
* immediately pass along the information that a CRender2D object needs to
|
||||||
|
* work with.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* Render2DPtr Pointer to a 2D renderer object.
|
||||||
|
*/
|
||||||
|
void AttachRenderer(CRender2D *Render2DPtr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Init(IRQObjectPtr):
|
||||||
|
*
|
||||||
|
* One-time initialization of the context. Must be called prior to all
|
||||||
|
* other members. Links the tile generator to an IRQ controller and
|
||||||
|
* allocates memory for tile generator RAM.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* IRQObjectPtr Pointer to the IRQ controller object.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* OKAY is successful, otherwise FAILED if a non-recoverable error
|
||||||
|
* occurred. Prints own error messages.
|
||||||
|
*/
|
||||||
|
BOOL Init(CIRQ *IRQObjectPtr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CTileGen(void):
|
||||||
|
* ~CTileGen(void):
|
||||||
|
*
|
||||||
|
* Constructor and destructor.
|
||||||
|
*/
|
||||||
|
CTileGen(void);
|
||||||
|
~CTileGen(void);
|
||||||
|
|
||||||
|
private:
|
||||||
|
CIRQ *IRQ; // IRQ controller the tile generator is attached to
|
||||||
|
CRender2D *Render2D; // 2D renderer the tile generator is attached to
|
||||||
|
|
||||||
|
// Tile generator VRAM
|
||||||
|
UINT8 *memoryPool; // all memory allocated here
|
||||||
|
|
||||||
|
// Registers
|
||||||
|
UINT32 regs[64];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // INCLUDED_TILEGEN_H
|
1097
OSD/SDL/Main.cpp
Normal file
1097
OSD/SDL/Main.cpp
Normal file
File diff suppressed because it is too large
Load diff
386
OSD/SDL/SDLInputSystem.cpp
Normal file
386
OSD/SDL/SDLInputSystem.cpp
Normal file
|
@ -0,0 +1,386 @@
|
||||||
|
#include "SDLInputSystem.h"
|
||||||
|
#include "Supermodel.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
SDLKeyMapStruct CSDLInputSystem::s_keyMap[] =
|
||||||
|
{
|
||||||
|
// General keys
|
||||||
|
{ "BACKSPACE", SDLK_BACKSPACE },
|
||||||
|
{ "TAB", SDLK_TAB },
|
||||||
|
{ "CLEAR", SDLK_CLEAR },
|
||||||
|
{ "RETURN", SDLK_RETURN },
|
||||||
|
{ "PAUSE", SDLK_PAUSE },
|
||||||
|
{ "ESCAPE", SDLK_ESCAPE },
|
||||||
|
{ "SPACE", SDLK_SPACE },
|
||||||
|
{ "EXCLAIM", SDLK_EXCLAIM },
|
||||||
|
{ "DBLQUOTE", SDLK_QUOTEDBL },
|
||||||
|
{ "HASH", SDLK_HASH },
|
||||||
|
{ "DOLLAR", SDLK_DOLLAR },
|
||||||
|
{ "AMPERSAND", SDLK_AMPERSAND },
|
||||||
|
{ "QUOTE", SDLK_QUOTE },
|
||||||
|
{ "LEFTPAREN", SDLK_LEFTPAREN },
|
||||||
|
{ "RIGHTPAREN", SDLK_RIGHTPAREN },
|
||||||
|
{ "ASTERISK", SDLK_ASTERISK },
|
||||||
|
{ "PLUS", SDLK_PLUS },
|
||||||
|
{ "COMMA", SDLK_COMMA },
|
||||||
|
{ "MINUS", SDLK_MINUS },
|
||||||
|
{ "PERIOD", SDLK_PERIOD },
|
||||||
|
{ "SLASH", SDLK_SLASH },
|
||||||
|
{ "0", SDLK_0 },
|
||||||
|
{ "1", SDLK_1 },
|
||||||
|
{ "2", SDLK_2 },
|
||||||
|
{ "3", SDLK_3 },
|
||||||
|
{ "4", SDLK_4 },
|
||||||
|
{ "5", SDLK_5 },
|
||||||
|
{ "6", SDLK_6 },
|
||||||
|
{ "7", SDLK_7 },
|
||||||
|
{ "8", SDLK_8 },
|
||||||
|
{ "9", SDLK_9 },
|
||||||
|
{ "COLON", SDLK_COLON },
|
||||||
|
{ "SEMICOLON", SDLK_SEMICOLON },
|
||||||
|
{ "LESS", SDLK_LESS },
|
||||||
|
{ "EQUALS", SDLK_EQUALS },
|
||||||
|
{ "GREATER", SDLK_GREATER },
|
||||||
|
{ "QUESTION", SDLK_QUESTION },
|
||||||
|
{ "AT", SDLK_AT },
|
||||||
|
{ "LEFTBRACKET", SDLK_LEFTBRACKET },
|
||||||
|
{ "BACKSLASH", SDLK_BACKSLASH },
|
||||||
|
{ "RIGHTBRACKET", SDLK_RIGHTBRACKET },
|
||||||
|
{ "CARET", SDLK_CARET },
|
||||||
|
{ "UNDERSCORE", SDLK_UNDERSCORE },
|
||||||
|
{ "BACKQUOTE", SDLK_BACKQUOTE },
|
||||||
|
{ "A", SDLK_a },
|
||||||
|
{ "B", SDLK_b },
|
||||||
|
{ "C", SDLK_c },
|
||||||
|
{ "D", SDLK_d },
|
||||||
|
{ "E", SDLK_e },
|
||||||
|
{ "F", SDLK_f },
|
||||||
|
{ "G", SDLK_g },
|
||||||
|
{ "H", SDLK_h },
|
||||||
|
{ "I", SDLK_i },
|
||||||
|
{ "J", SDLK_j },
|
||||||
|
{ "K", SDLK_k },
|
||||||
|
{ "L", SDLK_l },
|
||||||
|
{ "M", SDLK_m },
|
||||||
|
{ "N", SDLK_n },
|
||||||
|
{ "O", SDLK_o },
|
||||||
|
{ "P", SDLK_p },
|
||||||
|
{ "Q", SDLK_q },
|
||||||
|
{ "R", SDLK_r },
|
||||||
|
{ "S", SDLK_s },
|
||||||
|
{ "T", SDLK_t },
|
||||||
|
{ "U", SDLK_u },
|
||||||
|
{ "V", SDLK_v },
|
||||||
|
{ "W", SDLK_w },
|
||||||
|
{ "X", SDLK_x },
|
||||||
|
{ "Y", SDLK_y },
|
||||||
|
{ "Z", SDLK_z },
|
||||||
|
{ "DEL", SDLK_DELETE },
|
||||||
|
|
||||||
|
// Keypad
|
||||||
|
{ "KEYPAD0", SDLK_KP0 },
|
||||||
|
{ "KEYPAD1", SDLK_KP1 },
|
||||||
|
{ "KEYPAD2", SDLK_KP2 },
|
||||||
|
{ "KEYPAD3", SDLK_KP3 },
|
||||||
|
{ "KEYPAD4", SDLK_KP4 },
|
||||||
|
{ "KEYPAD5", SDLK_KP5 },
|
||||||
|
{ "KEYPAD6", SDLK_KP6 },
|
||||||
|
{ "KEYPAD7", SDLK_KP7 },
|
||||||
|
{ "KEYPAD8", SDLK_KP8 },
|
||||||
|
{ "KEYPAD9", SDLK_KP9 },
|
||||||
|
{ "KEYPADPERIOD", SDLK_KP_PERIOD },
|
||||||
|
{ "KEYPADDIVIDE", SDLK_KP_DIVIDE },
|
||||||
|
{ "KEYPADMULTIPLY", SDLK_KP_MULTIPLY },
|
||||||
|
{ "KEYPADMINUS", SDLK_KP_MINUS },
|
||||||
|
{ "KEYPADPLUS", SDLK_KP_PLUS },
|
||||||
|
{ "KEYPADENTER", SDLK_KP_ENTER },
|
||||||
|
{ "KEYPADEQUALS", SDLK_KP_EQUALS },
|
||||||
|
|
||||||
|
// Arrows + Home/End Pad
|
||||||
|
{ "UP", SDLK_UP },
|
||||||
|
{ "DOWN", SDLK_DOWN },
|
||||||
|
{ "RIGHT", SDLK_RIGHT },
|
||||||
|
{ "LEFT", SDLK_LEFT },
|
||||||
|
{ "INSERT", SDLK_INSERT },
|
||||||
|
{ "HOME", SDLK_HOME },
|
||||||
|
{ "END", SDLK_END },
|
||||||
|
{ "PGUP", SDLK_PAGEUP },
|
||||||
|
{ "PGDN", SDLK_PAGEDOWN },
|
||||||
|
|
||||||
|
// Function Key
|
||||||
|
{ "F1", SDLK_F1 },
|
||||||
|
{ "F2", SDLK_F2 },
|
||||||
|
{ "F3", SDLK_F3 },
|
||||||
|
{ "F4", SDLK_F4 },
|
||||||
|
{ "F5", SDLK_F5 },
|
||||||
|
{ "F6", SDLK_F6 },
|
||||||
|
{ "F7", SDLK_F7 },
|
||||||
|
{ "F8", SDLK_F8 },
|
||||||
|
{ "F9", SDLK_F9 },
|
||||||
|
{ "F10", SDLK_F10 },
|
||||||
|
{ "F11", SDLK_F11 },
|
||||||
|
{ "F12", SDLK_F12 },
|
||||||
|
{ "F13", SDLK_F13 },
|
||||||
|
{ "F14", SDLK_F14 },
|
||||||
|
{ "F15", SDLK_F15 },
|
||||||
|
|
||||||
|
// Modifier Keys
|
||||||
|
// Removed Numlock, Capslock and Scrollock as don't seem to be handled well by SDL
|
||||||
|
//{ "NUMLOCK", SDLK_NUMLOCK },
|
||||||
|
//{ "CAPSLOCK", SDLK_CAPSLOCK },
|
||||||
|
//{ "SCROLLLOCK", SDLK_SCROLLOCK },
|
||||||
|
{ "RIGHTSHIFT", SDLK_RSHIFT },
|
||||||
|
{ "LEFTSHIFT", SDLK_LSHIFT },
|
||||||
|
{ "RIGHTCTRL", SDLK_RCTRL },
|
||||||
|
{ "LEFTCTRL", SDLK_LCTRL },
|
||||||
|
{ "RIGHTALT", SDLK_RALT },
|
||||||
|
{ "LEFTALT", SDLK_LALT },
|
||||||
|
{ "RIGHTMETA", SDLK_RMETA },
|
||||||
|
{ "LEFTMETA", SDLK_LMETA },
|
||||||
|
{ "RIGHTWINDOWS", SDLK_RSUPER },
|
||||||
|
{ "LEFTWINDOWS", SDLK_LSUPER },
|
||||||
|
{ "ALTGR", SDLK_MODE },
|
||||||
|
{ "COMPOSE", SDLK_COMPOSE },
|
||||||
|
|
||||||
|
// Other
|
||||||
|
{ "HELP", SDLK_HELP },
|
||||||
|
{ "PRINT", SDLK_PRINT },
|
||||||
|
{ "SYSREQ", SDLK_SYSREQ },
|
||||||
|
{ "BREAK", SDLK_BREAK },
|
||||||
|
{ "MENU", SDLK_MENU },
|
||||||
|
{ "POWER", SDLK_POWER },
|
||||||
|
{ "EURO", SDLK_EURO },
|
||||||
|
{ "UNDO", SDLK_UNDO }
|
||||||
|
};
|
||||||
|
|
||||||
|
CSDLInputSystem::CSDLInputSystem() : CInputSystem("SDL"), m_keyState(NULL), m_mouseX(0), m_mouseY(0), m_mouseZ(0), m_mouseButtons(0)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
CSDLInputSystem::~CSDLInputSystem()
|
||||||
|
{
|
||||||
|
CloseJoysticks();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSDLInputSystem::OpenJoysticks()
|
||||||
|
{
|
||||||
|
// Open all available joysticks
|
||||||
|
int numJoys = SDL_NumJoysticks();
|
||||||
|
for (int joyNum = 0; joyNum < numJoys; joyNum++)
|
||||||
|
{
|
||||||
|
SDL_Joystick *joystick = SDL_JoystickOpen(joyNum);
|
||||||
|
if (joystick == NULL)
|
||||||
|
{
|
||||||
|
ErrorLog("Unable to open joystick device %d with SDL - skipping joystick.\n", joyNum + 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gather joystick details (num POVs & buttons and which axes are available)
|
||||||
|
int numAxes = SDL_JoystickNumAxes(joystick);
|
||||||
|
JoyDetails *joyDetails = new JoyDetails();
|
||||||
|
joyDetails->hasXAxis = numAxes > 0;
|
||||||
|
joyDetails->hasYAxis = numAxes > 1;
|
||||||
|
joyDetails->hasZAxis = numAxes > 2;
|
||||||
|
joyDetails->hasRXAxis = numAxes > 3;
|
||||||
|
joyDetails->hasRYAxis = numAxes > 4;
|
||||||
|
joyDetails->hasRZAxis = numAxes > 5;
|
||||||
|
joyDetails->numPOVs = SDL_JoystickNumHats(joystick);
|
||||||
|
joyDetails->numButtons = SDL_JoystickNumButtons(joystick);
|
||||||
|
|
||||||
|
m_joysticks.push_back(joystick);
|
||||||
|
m_joyDetails.push_back(joyDetails);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSDLInputSystem::CloseJoysticks()
|
||||||
|
{
|
||||||
|
// Close all previously opened joysticks
|
||||||
|
for (size_t i = 0; i < m_joysticks.size(); i++)
|
||||||
|
{
|
||||||
|
SDL_Joystick *joystick = m_joysticks[i];
|
||||||
|
JoyDetails *joyDetails = m_joyDetails[i];
|
||||||
|
|
||||||
|
SDL_JoystickClose(joystick);
|
||||||
|
delete joyDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_joysticks.clear();
|
||||||
|
m_joyDetails.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSDLInputSystem::InitializeSystem()
|
||||||
|
{
|
||||||
|
// Make sure joystick subsystem is initialized and joystick events are enabled
|
||||||
|
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) != 0)
|
||||||
|
{
|
||||||
|
ErrorLog("Unable to initialize SDL joystick subsystem (%s).\n", SDL_GetError());
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SDL_JoystickEventState(SDL_ENABLE);
|
||||||
|
|
||||||
|
// Open attached joysticks
|
||||||
|
OpenJoysticks();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSDLInputSystem::GetNumKeyboards()
|
||||||
|
{
|
||||||
|
// Return ANY_KEYBOARD as SDL 1.2 cannot handle multiple keyboards
|
||||||
|
return ANY_KEYBOARD;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSDLInputSystem::GetNumMice()
|
||||||
|
{
|
||||||
|
// Return ANY_MOUSE as SDL 1.2 cannot handle multiple mice
|
||||||
|
return ANY_MOUSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSDLInputSystem::GetNumJoysticks()
|
||||||
|
{
|
||||||
|
// Return number of joysticks found
|
||||||
|
return m_joysticks.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSDLInputSystem::GetKeyIndex(const char *keyName)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < NUM_SDL_KEYS; i++)
|
||||||
|
{
|
||||||
|
if (stricmp(keyName, s_keyMap[i].keyName) == 0)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *CSDLInputSystem::GetKeyName(int keyIndex)
|
||||||
|
{
|
||||||
|
if (keyIndex < 0 || keyIndex >= NUM_SDL_KEYS)
|
||||||
|
return NULL;
|
||||||
|
return s_keyMap[keyIndex].keyName;
|
||||||
|
}
|
||||||
|
|
||||||
|
JoyDetails *CSDLInputSystem::GetJoyDetails(int joyNum)
|
||||||
|
{
|
||||||
|
return m_joyDetails[joyNum];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSDLInputSystem::IsKeyPressed(int kbdNum, int keyIndex)
|
||||||
|
{
|
||||||
|
// Get SDL key for given index and check if currently pressed
|
||||||
|
SDLKey sdlKey = s_keyMap[keyIndex].sdlKey;
|
||||||
|
return !!m_keyState[sdlKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSDLInputSystem::GetMouseAxisValue(int mseNum, int axisNum)
|
||||||
|
{
|
||||||
|
// Return value for given mouse axis
|
||||||
|
switch (axisNum)
|
||||||
|
{
|
||||||
|
case AXIS_X: return m_mouseX;
|
||||||
|
case AXIS_Y: return m_mouseY;
|
||||||
|
case AXIS_Z: return m_mouseZ;
|
||||||
|
default: return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSDLInputSystem::GetMouseWheelDir(int mseNum)
|
||||||
|
{
|
||||||
|
// Return wheel value
|
||||||
|
return m_mouseWheelDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSDLInputSystem::IsMouseButPressed(int mseNum, int butNum)
|
||||||
|
{
|
||||||
|
// Return value for given mouse button
|
||||||
|
switch (butNum)
|
||||||
|
{
|
||||||
|
case 0: return !!(m_mouseButtons & SDL_BUTTON_LMASK);
|
||||||
|
case 1: return !!(m_mouseButtons & SDL_BUTTON_MMASK);
|
||||||
|
case 2: return !!(m_mouseButtons & SDL_BUTTON_RMASK);
|
||||||
|
case 3: return !!(m_mouseButtons & SDL_BUTTON_X1MASK);
|
||||||
|
case 4: return !!(m_mouseButtons & SDL_BUTTON_X2MASK);
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSDLInputSystem::GetJoyAxisValue(int joyNum, int axisNum)
|
||||||
|
{
|
||||||
|
// Get raw joystick axis value for given joystick from SDL (values range from -32768 to 32767)
|
||||||
|
SDL_Joystick *joystick = m_joysticks[joyNum];
|
||||||
|
return SDL_JoystickGetAxis(joystick, axisNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSDLInputSystem::IsJoyPOVInDir(int joyNum, int povNum, int povDir)
|
||||||
|
{
|
||||||
|
// Get current joystick POV-hat value for given joystick and POV number from SDL and check if pointing in required direction
|
||||||
|
SDL_Joystick *joystick = m_joysticks[joyNum];
|
||||||
|
int hatVal = SDL_JoystickGetHat(joystick, povNum);
|
||||||
|
switch (povDir)
|
||||||
|
{
|
||||||
|
case POV_UP: return !!(hatVal & SDL_HAT_UP);
|
||||||
|
case POV_DOWN: return !!(hatVal & SDL_HAT_DOWN);
|
||||||
|
case POV_LEFT: return !!(hatVal & SDL_HAT_LEFT);
|
||||||
|
case POV_RIGHT: return !!(hatVal & SDL_HAT_RIGHT);
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSDLInputSystem::IsJoyButPressed(int joyNum, int butNum)
|
||||||
|
{
|
||||||
|
// Get current joystick button state for given joystick and button number from SDL
|
||||||
|
SDL_Joystick *joystick = m_joysticks[joyNum];
|
||||||
|
return !!SDL_JoystickGetButton(joystick, butNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSDLInputSystem::Wait(int ms)
|
||||||
|
{
|
||||||
|
SDL_Delay(ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSDLInputSystem::SetMouseVisibility(bool visible)
|
||||||
|
{
|
||||||
|
SDL_ShowCursor(visible ? SDL_ENABLE : SDL_DISABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSDLInputSystem::Poll()
|
||||||
|
{
|
||||||
|
// Reset mouse wheel direction
|
||||||
|
m_mouseWheelDir = 0;
|
||||||
|
|
||||||
|
// Poll for event from SDL
|
||||||
|
SDL_Event e;
|
||||||
|
while (SDL_PollEvent(&e))
|
||||||
|
{
|
||||||
|
if (e.type == SDL_QUIT)
|
||||||
|
return false;
|
||||||
|
else if (e.type == SDL_MOUSEBUTTONDOWN)
|
||||||
|
{
|
||||||
|
// Mouse wheel movements are not returned by SDL_GetMouseState below, so they are handled here instead
|
||||||
|
// The mouse absolute Z-axis value and mouse wheel direction value are updated appropriately
|
||||||
|
if (e.button.button == SDL_BUTTON_WHEELUP)
|
||||||
|
{
|
||||||
|
m_mouseZ += 5;
|
||||||
|
m_mouseWheelDir = 1;
|
||||||
|
}
|
||||||
|
else if (e.button.button == SDL_BUTTON_WHEELDOWN)
|
||||||
|
{
|
||||||
|
m_mouseZ -= 5;
|
||||||
|
m_mouseWheelDir = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get key state from SDL
|
||||||
|
m_keyState = SDL_GetKeyState(NULL);
|
||||||
|
|
||||||
|
// Get mouse state from SDL (except mouse wheel which was handled earlier)
|
||||||
|
m_mouseButtons = SDL_GetMouseState(&m_mouseX, &m_mouseY);
|
||||||
|
|
||||||
|
// Update joystick state (not required as called implicitly by SDL_PollEvent above)
|
||||||
|
//SDL_JoystickUpdate();
|
||||||
|
return true;
|
||||||
|
}
|
106
OSD/SDL/SDLInputSystem.h
Normal file
106
OSD/SDL/SDLInputSystem.h
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
#ifndef INCLUDED_SDLINPUTSYSTEM_H
|
||||||
|
#define INCLUDED_SDLINPUTSYSTEM_H
|
||||||
|
|
||||||
|
#include "Types.h"
|
||||||
|
#include "Inputs/InputSource.h"
|
||||||
|
#include "Inputs/InputSystem.h"
|
||||||
|
|
||||||
|
#ifdef SUPERMODEL_OSX
|
||||||
|
#include <SDL/SDL.h>
|
||||||
|
#else
|
||||||
|
#include <SDL.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#define NUM_SDL_KEYS (sizeof(s_keyMap) / sizeof(SDLKeyMapStruct))
|
||||||
|
|
||||||
|
struct SDLKeyMapStruct
|
||||||
|
{
|
||||||
|
const char *keyName;
|
||||||
|
SDLKey sdlKey;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Input system that uses SDL.
|
||||||
|
*/
|
||||||
|
class CSDLInputSystem : public CInputSystem
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// Lookup table to map key names to SDLKeys
|
||||||
|
static SDLKeyMapStruct s_keyMap[];
|
||||||
|
|
||||||
|
// Vector to keep track of attached joysticks
|
||||||
|
vector<SDL_Joystick*> m_joysticks;
|
||||||
|
|
||||||
|
vector<JoyDetails*> m_joyDetails;
|
||||||
|
|
||||||
|
// Current key state obtained from SDL
|
||||||
|
Uint8 *m_keyState;
|
||||||
|
|
||||||
|
// Current mouse state obtained from SDL
|
||||||
|
int m_mouseX;
|
||||||
|
int m_mouseY;
|
||||||
|
int m_mouseZ;
|
||||||
|
short m_mouseWheelDir;
|
||||||
|
Uint8 m_mouseButtons;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Opens all attached joysticks.
|
||||||
|
*/
|
||||||
|
void OpenJoysticks();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Closes all attached joysticks.
|
||||||
|
*/
|
||||||
|
void CloseJoysticks();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/*
|
||||||
|
* Initializes the SDL input system.
|
||||||
|
*/
|
||||||
|
bool InitializeSystem();
|
||||||
|
|
||||||
|
int GetNumKeyboards();
|
||||||
|
|
||||||
|
int GetNumMice();
|
||||||
|
|
||||||
|
int GetNumJoysticks();
|
||||||
|
|
||||||
|
int GetKeyIndex(const char *keyName);
|
||||||
|
|
||||||
|
const char *GetKeyName(int keyIndex);
|
||||||
|
|
||||||
|
JoyDetails *GetJoyDetails(int joyNum);
|
||||||
|
|
||||||
|
bool IsKeyPressed(int kbdNum, int keyIndex);
|
||||||
|
|
||||||
|
int GetMouseAxisValue(int mseNum, int axisNum);
|
||||||
|
|
||||||
|
int GetMouseWheelDir(int mseNum);
|
||||||
|
|
||||||
|
bool IsMouseButPressed(int mseNum, int butNum);
|
||||||
|
|
||||||
|
int GetJoyAxisValue(int joyNum, int axisNum);
|
||||||
|
|
||||||
|
bool IsJoyPOVInDir(int joyNum, int povNum, int povDir);
|
||||||
|
|
||||||
|
bool IsJoyButPressed(int joyNum, int butNum);
|
||||||
|
|
||||||
|
void Wait(int ms);
|
||||||
|
|
||||||
|
void SetMouseVisibility(bool visible);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
* Constructs an SDL input system.
|
||||||
|
*/
|
||||||
|
CSDLInputSystem();
|
||||||
|
|
||||||
|
~CSDLInputSystem();
|
||||||
|
|
||||||
|
bool Poll();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // INCLUDED_SDLINPUTSYSTEM_H
|
11
OSD/SDL/SDLMain_tmpl.h
Normal file
11
OSD/SDL/SDLMain_tmpl.h
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
/* SDLMain.m - main entry point for our Cocoa-ized SDL app
|
||||||
|
Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
|
||||||
|
Non-NIB-Code & other changes: Max Horn <max@quendi.de>
|
||||||
|
|
||||||
|
Feel free to customize this file to suit your needs
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
|
@interface SDLMain : NSObject
|
||||||
|
@end
|
384
OSD/SDL/SDLMain_tmpl.m
Normal file
384
OSD/SDL/SDLMain_tmpl.m
Normal file
|
@ -0,0 +1,384 @@
|
||||||
|
/* SDLMain.m - main entry point for our Cocoa-ized SDL app
|
||||||
|
Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
|
||||||
|
Non-NIB-Code & other changes: Max Horn <max@quendi.de>
|
||||||
|
|
||||||
|
Feel free to customize this file to suit your needs
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "SDL/SDL.h"
|
||||||
|
#import "SDLMain_tmpl.h"
|
||||||
|
#import <sys/param.h> /* for MAXPATHLEN */
|
||||||
|
#import <unistd.h>
|
||||||
|
|
||||||
|
/* For some reaon, Apple removed setAppleMenu from the headers in 10.4,
|
||||||
|
but the method still is there and works. To avoid warnings, we declare
|
||||||
|
it ourselves here. */
|
||||||
|
@interface NSApplication(SDL_Missing_Methods)
|
||||||
|
- (void)setAppleMenu:(NSMenu *)menu;
|
||||||
|
@end
|
||||||
|
|
||||||
|
/* Use this flag to determine whether we use SDLMain.nib or not */
|
||||||
|
#define SDL_USE_NIB_FILE 0
|
||||||
|
|
||||||
|
/* Use this flag to determine whether we use CPS (docking) or not */
|
||||||
|
#define SDL_USE_CPS 0
|
||||||
|
#ifdef SDL_USE_CPS
|
||||||
|
/* Portions of CPS.h */
|
||||||
|
typedef struct CPSProcessSerNum
|
||||||
|
{
|
||||||
|
UInt32 lo;
|
||||||
|
UInt32 hi;
|
||||||
|
} CPSProcessSerNum;
|
||||||
|
|
||||||
|
extern "C" OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn);
|
||||||
|
extern "C" OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
|
||||||
|
extern "C" OSErr CPSSetFrontProcess( CPSProcessSerNum *psn);
|
||||||
|
|
||||||
|
#endif /* SDL_USE_CPS */
|
||||||
|
|
||||||
|
static int gArgc;
|
||||||
|
static char **gArgv;
|
||||||
|
static BOOL gFinderLaunch;
|
||||||
|
static BOOL gCalledAppMainline = FALSE;
|
||||||
|
|
||||||
|
static NSString *getApplicationName(void)
|
||||||
|
{
|
||||||
|
NSDictionary *dict;
|
||||||
|
NSString *appName = 0;
|
||||||
|
|
||||||
|
/* Determine the application name */
|
||||||
|
dict = (NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle());
|
||||||
|
if (dict)
|
||||||
|
appName = [dict objectForKey: @"CFBundleName"];
|
||||||
|
|
||||||
|
if (![appName length])
|
||||||
|
appName = [[NSProcessInfo processInfo] processName];
|
||||||
|
|
||||||
|
return appName;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if SDL_USE_NIB_FILE
|
||||||
|
/* A helper category for NSString */
|
||||||
|
@interface NSString (ReplaceSubString)
|
||||||
|
- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString;
|
||||||
|
@end
|
||||||
|
#endif
|
||||||
|
|
||||||
|
@interface SDLApplication : NSApplication
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation SDLApplication
|
||||||
|
/* Invoked from the Quit menu item */
|
||||||
|
- (void)terminate:(id)sender
|
||||||
|
{
|
||||||
|
/* Post a SDL_QUIT event */
|
||||||
|
SDL_Event event;
|
||||||
|
event.type = SDL_QUIT;
|
||||||
|
SDL_PushEvent(&event);
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
/* The main class of the application, the application's delegate */
|
||||||
|
@implementation SDLMain
|
||||||
|
|
||||||
|
/* Set the working directory to the .app's parent directory */
|
||||||
|
- (void) setupWorkingDirectory:(BOOL)shouldChdir
|
||||||
|
{
|
||||||
|
if (shouldChdir)
|
||||||
|
{
|
||||||
|
char parentdir[MAXPATHLEN];
|
||||||
|
CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle());
|
||||||
|
CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url);
|
||||||
|
if (CFURLGetFileSystemRepresentation(url2, true, (UInt8 *)parentdir, MAXPATHLEN)) {
|
||||||
|
assert ( chdir (parentdir) == 0 ); /* chdir to the binary app's parent */
|
||||||
|
}
|
||||||
|
CFRelease(url);
|
||||||
|
CFRelease(url2);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#if SDL_USE_NIB_FILE
|
||||||
|
|
||||||
|
/* Fix menu to contain the real app name instead of "SDL App" */
|
||||||
|
- (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName
|
||||||
|
{
|
||||||
|
NSRange aRange;
|
||||||
|
NSEnumerator *enumerator;
|
||||||
|
NSMenuItem *menuItem;
|
||||||
|
|
||||||
|
aRange = [[aMenu title] rangeOfString:@"SDL App"];
|
||||||
|
if (aRange.length != 0)
|
||||||
|
[aMenu setTitle: [[aMenu title] stringByReplacingRange:aRange with:appName]];
|
||||||
|
|
||||||
|
enumerator = [[aMenu itemArray] objectEnumerator];
|
||||||
|
while ((menuItem = [enumerator nextObject]))
|
||||||
|
{
|
||||||
|
aRange = [[menuItem title] rangeOfString:@"SDL App"];
|
||||||
|
if (aRange.length != 0)
|
||||||
|
[menuItem setTitle: [[menuItem title] stringByReplacingRange:aRange with:appName]];
|
||||||
|
if ([menuItem hasSubmenu])
|
||||||
|
[self fixMenu:[menuItem submenu] withAppName:appName];
|
||||||
|
}
|
||||||
|
[ aMenu sizeToFit ];
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
static void setApplicationMenu(void)
|
||||||
|
{
|
||||||
|
/* warning: this code is very odd */
|
||||||
|
NSMenu *appleMenu;
|
||||||
|
NSMenuItem *menuItem;
|
||||||
|
NSString *title;
|
||||||
|
NSString *appName;
|
||||||
|
|
||||||
|
appName = getApplicationName();
|
||||||
|
appleMenu = [[NSMenu alloc] initWithTitle:@""];
|
||||||
|
|
||||||
|
/* Add menu items */
|
||||||
|
title = [@"About " stringByAppendingString:appName];
|
||||||
|
[appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
|
||||||
|
|
||||||
|
[appleMenu addItem:[NSMenuItem separatorItem]];
|
||||||
|
|
||||||
|
title = [@"Hide " stringByAppendingString:appName];
|
||||||
|
[appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
|
||||||
|
|
||||||
|
menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
|
||||||
|
[menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
|
||||||
|
|
||||||
|
[appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
|
||||||
|
|
||||||
|
[appleMenu addItem:[NSMenuItem separatorItem]];
|
||||||
|
|
||||||
|
title = [@"Quit " stringByAppendingString:appName];
|
||||||
|
[appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
|
||||||
|
|
||||||
|
|
||||||
|
/* Put menu into the menubar */
|
||||||
|
menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
|
||||||
|
[menuItem setSubmenu:appleMenu];
|
||||||
|
[[NSApp mainMenu] addItem:menuItem];
|
||||||
|
|
||||||
|
/* Tell the application object that this is now the application menu */
|
||||||
|
[NSApp setAppleMenu:appleMenu];
|
||||||
|
|
||||||
|
/* Finally give up our references to the objects */
|
||||||
|
[appleMenu release];
|
||||||
|
[menuItem release];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create a window menu */
|
||||||
|
static void setupWindowMenu(void)
|
||||||
|
{
|
||||||
|
NSMenu *windowMenu;
|
||||||
|
NSMenuItem *windowMenuItem;
|
||||||
|
NSMenuItem *menuItem;
|
||||||
|
|
||||||
|
windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
|
||||||
|
|
||||||
|
/* "Minimize" item */
|
||||||
|
menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
|
||||||
|
[windowMenu addItem:menuItem];
|
||||||
|
[menuItem release];
|
||||||
|
|
||||||
|
/* Put menu into the menubar */
|
||||||
|
windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
|
||||||
|
[windowMenuItem setSubmenu:windowMenu];
|
||||||
|
[[NSApp mainMenu] addItem:windowMenuItem];
|
||||||
|
|
||||||
|
/* Tell the application object that this is now the window menu */
|
||||||
|
[NSApp setWindowsMenu:windowMenu];
|
||||||
|
|
||||||
|
/* Finally give up our references to the objects */
|
||||||
|
[windowMenu release];
|
||||||
|
[windowMenuItem release];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Replacement for NSApplicationMain */
|
||||||
|
static void CustomApplicationMain (int argc, char **argv)
|
||||||
|
{
|
||||||
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||||
|
SDLMain *sdlMain;
|
||||||
|
|
||||||
|
/* Ensure the application object is initialised */
|
||||||
|
[SDLApplication sharedApplication];
|
||||||
|
|
||||||
|
#ifdef SDL_USE_CPS
|
||||||
|
{
|
||||||
|
CPSProcessSerNum PSN;
|
||||||
|
/* Tell the dock about us */
|
||||||
|
if (!CPSGetCurrentProcess(&PSN))
|
||||||
|
if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103))
|
||||||
|
if (!CPSSetFrontProcess(&PSN))
|
||||||
|
[SDLApplication sharedApplication];
|
||||||
|
}
|
||||||
|
#endif /* SDL_USE_CPS */
|
||||||
|
|
||||||
|
/* Set up the menubar */
|
||||||
|
[NSApp setMainMenu:[[NSMenu alloc] init]];
|
||||||
|
setApplicationMenu();
|
||||||
|
setupWindowMenu();
|
||||||
|
|
||||||
|
/* Create SDLMain and make it the app delegate */
|
||||||
|
sdlMain = [[SDLMain alloc] init];
|
||||||
|
[NSApp setDelegate:sdlMain];
|
||||||
|
|
||||||
|
/* Start the main event loop */
|
||||||
|
[NSApp run];
|
||||||
|
|
||||||
|
[sdlMain release];
|
||||||
|
[pool release];
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Catch document open requests...this lets us notice files when the app
|
||||||
|
* was launched by double-clicking a document, or when a document was
|
||||||
|
* dragged/dropped on the app's icon. You need to have a
|
||||||
|
* CFBundleDocumentsType section in your Info.plist to get this message,
|
||||||
|
* apparently.
|
||||||
|
*
|
||||||
|
* Files are added to gArgv, so to the app, they'll look like command line
|
||||||
|
* arguments. Previously, apps launched from the finder had nothing but
|
||||||
|
* an argv[0].
|
||||||
|
*
|
||||||
|
* This message may be received multiple times to open several docs on launch.
|
||||||
|
*
|
||||||
|
* This message is ignored once the app's mainline has been called.
|
||||||
|
*/
|
||||||
|
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
|
||||||
|
{
|
||||||
|
const char *temparg;
|
||||||
|
size_t arglen;
|
||||||
|
char *arg;
|
||||||
|
char **newargv;
|
||||||
|
|
||||||
|
if (!gFinderLaunch) /* MacOS is passing command line args. */
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (gCalledAppMainline) /* app has started, ignore this document. */
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
temparg = [filename UTF8String];
|
||||||
|
arglen = SDL_strlen(temparg) + 1;
|
||||||
|
arg = (char *) SDL_malloc(arglen);
|
||||||
|
if (arg == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
newargv = (char **) SDL_realloc(gArgv, sizeof (char *) * (gArgc + 2));
|
||||||
|
if (newargv == NULL)
|
||||||
|
{
|
||||||
|
SDL_free(arg);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
gArgv = newargv;
|
||||||
|
|
||||||
|
SDL_strlcpy(arg, temparg, arglen);
|
||||||
|
gArgv[gArgc++] = arg;
|
||||||
|
gArgv[gArgc] = NULL;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Called when the internal event loop has just started running */
|
||||||
|
- (void) applicationDidFinishLaunching: (NSNotification *) note
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
|
||||||
|
/* Set the working directory to the .app's parent directory */
|
||||||
|
[self setupWorkingDirectory:gFinderLaunch];
|
||||||
|
|
||||||
|
#if SDL_USE_NIB_FILE
|
||||||
|
/* Set the main menu to contain the real app name instead of "SDL App" */
|
||||||
|
[self fixMenu:[NSApp mainMenu] withAppName:getApplicationName()];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Hand off to main application code */
|
||||||
|
gCalledAppMainline = TRUE;
|
||||||
|
status = SDL_main (gArgc, gArgv);
|
||||||
|
|
||||||
|
/* We're done, thank you for playing */
|
||||||
|
exit(status);
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@implementation NSString (ReplaceSubString)
|
||||||
|
|
||||||
|
- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString
|
||||||
|
{
|
||||||
|
unsigned int bufferSize;
|
||||||
|
unsigned int selfLen = [self length];
|
||||||
|
unsigned int aStringLen = [aString length];
|
||||||
|
unichar *buffer;
|
||||||
|
NSRange localRange;
|
||||||
|
NSString *result;
|
||||||
|
|
||||||
|
bufferSize = selfLen + aStringLen - aRange.length;
|
||||||
|
buffer = (unichar *)NSAllocateMemoryPages(bufferSize*sizeof(unichar));
|
||||||
|
|
||||||
|
/* Get first part into buffer */
|
||||||
|
localRange.location = 0;
|
||||||
|
localRange.length = aRange.location;
|
||||||
|
[self getCharacters:buffer range:localRange];
|
||||||
|
|
||||||
|
/* Get middle part into buffer */
|
||||||
|
localRange.location = 0;
|
||||||
|
localRange.length = aStringLen;
|
||||||
|
[aString getCharacters:(buffer+aRange.location) range:localRange];
|
||||||
|
|
||||||
|
/* Get last part into buffer */
|
||||||
|
localRange.location = aRange.location + aRange.length;
|
||||||
|
localRange.length = selfLen - localRange.location;
|
||||||
|
[self getCharacters:(buffer+aRange.location+aStringLen) range:localRange];
|
||||||
|
|
||||||
|
/* Build output string */
|
||||||
|
result = [NSString stringWithCharacters:buffer length:bufferSize];
|
||||||
|
|
||||||
|
NSDeallocateMemoryPages(buffer, bufferSize);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef main
|
||||||
|
# undef main
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Main entry point to executable - should *not* be SDL_main! */
|
||||||
|
int main (int argc, char **argv)
|
||||||
|
{
|
||||||
|
/* Copy the arguments into a global variable */
|
||||||
|
/* This is passed if we are launched by double-clicking */
|
||||||
|
if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) {
|
||||||
|
gArgv = (char **) SDL_malloc(sizeof (char *) * 2);
|
||||||
|
gArgv[0] = argv[0];
|
||||||
|
gArgv[1] = NULL;
|
||||||
|
gArgc = 1;
|
||||||
|
gFinderLaunch = YES;
|
||||||
|
} else {
|
||||||
|
int i;
|
||||||
|
gArgc = argc;
|
||||||
|
gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1));
|
||||||
|
for (i = 0; i <= argc; i++)
|
||||||
|
gArgv[i] = argv[i];
|
||||||
|
gFinderLaunch = NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if SDL_USE_NIB_FILE
|
||||||
|
[SDLApplication poseAsClass:[NSApplication class]];
|
||||||
|
NSApplicationMain (argc, argv);
|
||||||
|
#else
|
||||||
|
CustomApplicationMain (argc, argv);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
51
OSD/SDL/Types.h
Normal file
51
OSD/SDL/Types.h
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Types.h
|
||||||
|
*
|
||||||
|
* Fundamental data types.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDED_TYPES_H
|
||||||
|
#define INCLUDED_TYPES_H
|
||||||
|
|
||||||
|
// Booleans (must be 0 or 1 only)
|
||||||
|
#define TRUE 1
|
||||||
|
#define FALSE 0
|
||||||
|
#define OKAY FALSE
|
||||||
|
#define FAIL TRUE
|
||||||
|
|
||||||
|
// Types
|
||||||
|
typedef int BOOL;
|
||||||
|
typedef unsigned long long UINT64;
|
||||||
|
typedef signed long long INT64;
|
||||||
|
typedef unsigned int UINT32;
|
||||||
|
typedef signed int INT32;
|
||||||
|
typedef unsigned short int UINT16;
|
||||||
|
typedef signed short int INT16;
|
||||||
|
typedef unsigned char UINT8;
|
||||||
|
typedef signed char INT8;
|
||||||
|
typedef float FLOAT32;
|
||||||
|
typedef double FLOAT64;
|
||||||
|
|
||||||
|
|
||||||
|
#endif // INCLUDED_TYPES_H
|
1135
OSD/Windows/DirectInputSystem.cpp
Normal file
1135
OSD/Windows/DirectInputSystem.cpp
Normal file
File diff suppressed because it is too large
Load diff
176
OSD/Windows/DirectInputSystem.h
Normal file
176
OSD/Windows/DirectInputSystem.h
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
#ifndef INCLUDED_DIRECTINPUTSYSTEM_H
|
||||||
|
#define INCLUDED_DIRECTINPUTSYSTEM_H
|
||||||
|
|
||||||
|
#include "Types.h"
|
||||||
|
#include "Inputs/InputSource.h"
|
||||||
|
#include "Inputs/InputSystem.h"
|
||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
#include <dinput.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#define NUM_DI_KEYS (sizeof(s_keyMap) / sizeof(DIKeyMapStruct))
|
||||||
|
|
||||||
|
#define MATCHES_USAGE(wUsage, wUsagePage, word) wUsage == LOWORD(word) && wUsagePage == HIWORD(word)
|
||||||
|
#define X_AXIS_USAGE 0x00010030
|
||||||
|
#define Y_AXIS_USAGE 0x00010031
|
||||||
|
#define Z_AXIS_USAGE 0x00010032
|
||||||
|
#define RX_AXIS_USAGE 0x00010033
|
||||||
|
#define RY_AXIS_USAGE 0x00010034
|
||||||
|
#define RZ_AXIS_USAGE 0x00010035
|
||||||
|
|
||||||
|
struct DIKeyMapStruct
|
||||||
|
{
|
||||||
|
const char *keyName;
|
||||||
|
int diKey;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RawMseState
|
||||||
|
{
|
||||||
|
LONG x;
|
||||||
|
LONG y;
|
||||||
|
LONG z;
|
||||||
|
LONG wheelDelta;
|
||||||
|
SHORT wheelDir;
|
||||||
|
USHORT buttons;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DIMseState
|
||||||
|
{
|
||||||
|
LONG x;
|
||||||
|
LONG y;
|
||||||
|
LONG z;
|
||||||
|
SHORT wheelDir;
|
||||||
|
BYTE buttons[5];
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef /*WINUSERAPI*/ INT (WINAPI *GetRawInputDeviceListPtr)(OUT PRAWINPUTDEVICELIST pRawInputDeviceList, IN OUT PUINT puiNumDevices, IN UINT cbSize);
|
||||||
|
typedef /*WINUSERAPI*/ INT (WINAPI *GetRawInputDeviceInfoPtr)(IN HANDLE hDevice, IN UINT uiCommand, OUT LPVOID pData, IN OUT PUINT pcbSize);
|
||||||
|
typedef /*WINUSERAPI*/ BOOL (WINAPI *RegisterRawInputDevicesPtr)(IN PCRAWINPUTDEVICE pRawInputDevices, IN UINT uiNumDevices, IN UINT cbSize);
|
||||||
|
typedef /*WINUSERAPI*/ INT (WINAPI *GetRawInputDataPtr)(IN HRAWINPUT hRawInput, IN UINT uiCommand, OUT LPVOID pData, IN OUT PUINT pcbSize, IN UINT cbSizeHeader);
|
||||||
|
|
||||||
|
LRESULT WINAPI CallWndProc(int nCode, WPARAM wParam, LPARAM lParam);
|
||||||
|
|
||||||
|
LRESULT WINAPI GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam);
|
||||||
|
|
||||||
|
BOOL CALLBACK DI8EnumDevicesCallback(LPCDIDEVICEINSTANCE instance, LPVOID context);
|
||||||
|
|
||||||
|
BOOL CALLBACK DI8EnumAxesCallback(LPDIDEVICEOBJECTINSTANCE instance, LPVOID context);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Input system that uses DirectInput and RawInput APIs.
|
||||||
|
*/
|
||||||
|
class CDirectInputSystem : public CInputSystem
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// Lookup table to map key names to DirectInput keycodes and Virtual keycodes
|
||||||
|
static DIKeyMapStruct s_keyMap[];
|
||||||
|
|
||||||
|
bool m_useRawInput;
|
||||||
|
|
||||||
|
HWND m_hwnd;
|
||||||
|
|
||||||
|
bool m_activated;
|
||||||
|
|
||||||
|
GetRawInputDeviceListPtr m_getRIDevListPtr;
|
||||||
|
GetRawInputDeviceInfoPtr m_getRIDevInfoPtr;
|
||||||
|
RegisterRawInputDevicesPtr m_regRIDevsPtr;
|
||||||
|
GetRawInputDataPtr m_getRIDataPtr;
|
||||||
|
|
||||||
|
vector<HANDLE> m_rawKeyboards;
|
||||||
|
vector<BOOL*> m_rawKeyStates;
|
||||||
|
vector<HANDLE> m_rawMice;
|
||||||
|
RawMseState m_combRawMseState;
|
||||||
|
vector<RawMseState*> m_rawMseStates;
|
||||||
|
|
||||||
|
LPDIRECTINPUT8 m_di8;
|
||||||
|
LPDIRECTINPUTDEVICE8 m_di8Keyboard;
|
||||||
|
LPDIRECTINPUTDEVICE8 m_di8Mouse;
|
||||||
|
vector<LPDIRECTINPUTDEVICE8> m_di8Joysticks;
|
||||||
|
|
||||||
|
vector<JoyDetails*> m_joyDetails;
|
||||||
|
|
||||||
|
BYTE m_diKeyState[256];
|
||||||
|
DIMseState m_diMseState;
|
||||||
|
vector<LPDIJOYSTATE2> m_diJoyStates;
|
||||||
|
|
||||||
|
void OpenKeyboardsAndMice();
|
||||||
|
|
||||||
|
void ActivateKeyboardsAndMice();
|
||||||
|
|
||||||
|
void PollKeyboardsAndMice();
|
||||||
|
|
||||||
|
void CloseKeyboardsAndMice();
|
||||||
|
|
||||||
|
void ResetMice();
|
||||||
|
|
||||||
|
void ProcessRawInput(HRAWINPUT hInput);
|
||||||
|
|
||||||
|
void OpenJoysticks();
|
||||||
|
|
||||||
|
void ActivateJoysticks();
|
||||||
|
|
||||||
|
void PollJoysticks();
|
||||||
|
|
||||||
|
void CloseJoysticks();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/*
|
||||||
|
* Initializes the DirectInput input system.
|
||||||
|
*/
|
||||||
|
bool InitializeSystem();
|
||||||
|
|
||||||
|
int GetNumKeyboards();
|
||||||
|
|
||||||
|
int GetNumMice();
|
||||||
|
|
||||||
|
int GetNumJoysticks();
|
||||||
|
|
||||||
|
int GetKeyIndex(const char *keyName);
|
||||||
|
|
||||||
|
const char *GetKeyName(int keyIndex);
|
||||||
|
|
||||||
|
JoyDetails *GetJoyDetails(int joyNum);
|
||||||
|
|
||||||
|
bool IsKeyPressed(int joyNum, int keyIndex);
|
||||||
|
|
||||||
|
int GetMouseAxisValue(int mseNum, int axisNum);
|
||||||
|
|
||||||
|
int GetMouseWheelDir(int mseNum);
|
||||||
|
|
||||||
|
bool IsMouseButPressed(int mseNum, int butNum);
|
||||||
|
|
||||||
|
int GetJoyAxisValue(int joyNum, int axisNum);
|
||||||
|
|
||||||
|
bool IsJoyPOVInDir(int joyNum, int povNum, int povDir);
|
||||||
|
|
||||||
|
bool IsJoyButPressed(int joyNum, int butNum);
|
||||||
|
|
||||||
|
void Wait(int ms);
|
||||||
|
|
||||||
|
bool ConfigMouseCentered();
|
||||||
|
|
||||||
|
CInputSource *CreateAnyMouseSource(EMousePart msePart);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
* Constructs a DirectInput/RawInput input system.
|
||||||
|
* If useRawInput is true then RawInput is used for keyboard and mice movements (allowing multiple devices). If false
|
||||||
|
* then DirectInput is used instead (which doesn't allow multiple devices).
|
||||||
|
* In both cases, DirectInput is used for reading joysticks.
|
||||||
|
*/
|
||||||
|
CDirectInputSystem(bool useRawInput);
|
||||||
|
|
||||||
|
~CDirectInputSystem();
|
||||||
|
|
||||||
|
bool Poll();
|
||||||
|
|
||||||
|
void SetMouseVisibility(bool visible);
|
||||||
|
|
||||||
|
void ConfigEnd();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // INCLUDED_DIRECTINPUTSYSTEM_H
|
15201
Pkgs/glew.c
Normal file
15201
Pkgs/glew.c
Normal file
File diff suppressed because it is too large
Load diff
15343
Pkgs/glew.h
Normal file
15343
Pkgs/glew.h
Normal file
File diff suppressed because it is too large
Load diff
1537
Pkgs/glxew.h
Normal file
1537
Pkgs/glxew.h
Normal file
File diff suppressed because it is too large
Load diff
235
Pkgs/ioapi.c
Normal file
235
Pkgs/ioapi.c
Normal file
|
@ -0,0 +1,235 @@
|
||||||
|
/* ioapi.h -- IO base function header for compress/uncompress .zip
|
||||||
|
part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )
|
||||||
|
|
||||||
|
Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )
|
||||||
|
|
||||||
|
Modifications for Zip64 support
|
||||||
|
Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )
|
||||||
|
|
||||||
|
For more info read MiniZip_info.txt
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if (defined(_WIN32))
|
||||||
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "ioapi.h"
|
||||||
|
|
||||||
|
voidpf call_zopen64 (const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode)
|
||||||
|
{
|
||||||
|
if (pfilefunc->zfile_func64.zopen64_file != NULL)
|
||||||
|
return (*(pfilefunc->zfile_func64.zopen64_file)) (pfilefunc->zfile_func64.opaque,filename,mode);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (*(pfilefunc->zopen32_file))(pfilefunc->zfile_func64.opaque,(const char*)filename,mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
long call_zseek64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin)
|
||||||
|
{
|
||||||
|
if (pfilefunc->zfile_func64.zseek64_file != NULL)
|
||||||
|
return (*(pfilefunc->zfile_func64.zseek64_file)) (pfilefunc->zfile_func64.opaque,filestream,offset,origin);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uLong offsetTruncated = (uLong)offset;
|
||||||
|
if (offsetTruncated != offset)
|
||||||
|
return -1;
|
||||||
|
else
|
||||||
|
return (*(pfilefunc->zseek32_file))(pfilefunc->zfile_func64.opaque,filestream,offsetTruncated,origin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ZPOS64_T call_ztell64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream)
|
||||||
|
{
|
||||||
|
if (pfilefunc->zfile_func64.zseek64_file != NULL)
|
||||||
|
return (*(pfilefunc->zfile_func64.ztell64_file)) (pfilefunc->zfile_func64.opaque,filestream);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uLong tell_uLong = (*(pfilefunc->ztell32_file))(pfilefunc->zfile_func64.opaque,filestream);
|
||||||
|
if ((tell_uLong) == ((uLong)-1))
|
||||||
|
return (ZPOS64_T)-1;
|
||||||
|
else
|
||||||
|
return tell_uLong;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32)
|
||||||
|
{
|
||||||
|
p_filefunc64_32->zfile_func64.zopen64_file = NULL;
|
||||||
|
p_filefunc64_32->zopen32_file = p_filefunc32->zopen_file;
|
||||||
|
p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file;
|
||||||
|
p_filefunc64_32->zfile_func64.zread_file = p_filefunc32->zread_file;
|
||||||
|
p_filefunc64_32->zfile_func64.zwrite_file = p_filefunc32->zwrite_file;
|
||||||
|
p_filefunc64_32->zfile_func64.ztell64_file = NULL;
|
||||||
|
p_filefunc64_32->zfile_func64.zseek64_file = NULL;
|
||||||
|
p_filefunc64_32->zfile_func64.zclose_file = p_filefunc32->zclose_file;
|
||||||
|
p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file;
|
||||||
|
p_filefunc64_32->zfile_func64.opaque = p_filefunc32->opaque;
|
||||||
|
p_filefunc64_32->zseek32_file = p_filefunc32->zseek_file;
|
||||||
|
p_filefunc64_32->ztell32_file = p_filefunc32->ztell_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static voidpf ZCALLBACK fopen_file_func OF((voidpf opaque, const char* filename, int mode));
|
||||||
|
static uLong ZCALLBACK fread_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size));
|
||||||
|
static uLong ZCALLBACK fwrite_file_func OF((voidpf opaque, voidpf stream, const void* buf,uLong size));
|
||||||
|
static ZPOS64_T ZCALLBACK ftell64_file_func OF((voidpf opaque, voidpf stream));
|
||||||
|
static long ZCALLBACK fseek64_file_func OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin));
|
||||||
|
static int ZCALLBACK fclose_file_func OF((voidpf opaque, voidpf stream));
|
||||||
|
static int ZCALLBACK ferror_file_func OF((voidpf opaque, voidpf stream));
|
||||||
|
|
||||||
|
static voidpf ZCALLBACK fopen_file_func (voidpf opaque, const char* filename, int mode)
|
||||||
|
{
|
||||||
|
FILE* file = NULL;
|
||||||
|
const char* mode_fopen = NULL;
|
||||||
|
if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ)
|
||||||
|
mode_fopen = "rb";
|
||||||
|
else
|
||||||
|
if (mode & ZLIB_FILEFUNC_MODE_EXISTING)
|
||||||
|
mode_fopen = "r+b";
|
||||||
|
else
|
||||||
|
if (mode & ZLIB_FILEFUNC_MODE_CREATE)
|
||||||
|
mode_fopen = "wb";
|
||||||
|
|
||||||
|
if ((filename!=NULL) && (mode_fopen != NULL))
|
||||||
|
file = fopen(filename, mode_fopen);
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
static voidpf ZCALLBACK fopen64_file_func (voidpf opaque, const void* filename, int mode)
|
||||||
|
{
|
||||||
|
FILE* file = NULL;
|
||||||
|
const char* mode_fopen = NULL;
|
||||||
|
if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ)
|
||||||
|
mode_fopen = "rb";
|
||||||
|
else
|
||||||
|
if (mode & ZLIB_FILEFUNC_MODE_EXISTING)
|
||||||
|
mode_fopen = "r+b";
|
||||||
|
else
|
||||||
|
if (mode & ZLIB_FILEFUNC_MODE_CREATE)
|
||||||
|
mode_fopen = "wb";
|
||||||
|
|
||||||
|
if ((filename!=NULL) && (mode_fopen != NULL))
|
||||||
|
file = fopen64((const char*)filename, mode_fopen);
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uLong ZCALLBACK fread_file_func (voidpf opaque, voidpf stream, void* buf, uLong size)
|
||||||
|
{
|
||||||
|
uLong ret;
|
||||||
|
ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uLong ZCALLBACK fwrite_file_func (voidpf opaque, voidpf stream, const void* buf, uLong size)
|
||||||
|
{
|
||||||
|
uLong ret;
|
||||||
|
ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long ZCALLBACK ftell_file_func (voidpf opaque, voidpf stream)
|
||||||
|
{
|
||||||
|
long ret;
|
||||||
|
ret = ftell((FILE *)stream);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ZPOS64_T ZCALLBACK ftell64_file_func (voidpf opaque, voidpf stream)
|
||||||
|
{
|
||||||
|
ZPOS64_T ret;
|
||||||
|
ret = ftello64((FILE *)stream);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long ZCALLBACK fseek_file_func (voidpf opaque, voidpf stream, uLong offset, int origin)
|
||||||
|
{
|
||||||
|
int fseek_origin=0;
|
||||||
|
long ret;
|
||||||
|
switch (origin)
|
||||||
|
{
|
||||||
|
case ZLIB_FILEFUNC_SEEK_CUR :
|
||||||
|
fseek_origin = SEEK_CUR;
|
||||||
|
break;
|
||||||
|
case ZLIB_FILEFUNC_SEEK_END :
|
||||||
|
fseek_origin = SEEK_END;
|
||||||
|
break;
|
||||||
|
case ZLIB_FILEFUNC_SEEK_SET :
|
||||||
|
fseek_origin = SEEK_SET;
|
||||||
|
break;
|
||||||
|
default: return -1;
|
||||||
|
}
|
||||||
|
ret = 0;
|
||||||
|
if (fseek((FILE *)stream, offset, fseek_origin) != 0)
|
||||||
|
ret = -1;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long ZCALLBACK fseek64_file_func (voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)
|
||||||
|
{
|
||||||
|
int fseek_origin=0;
|
||||||
|
long ret;
|
||||||
|
switch (origin)
|
||||||
|
{
|
||||||
|
case ZLIB_FILEFUNC_SEEK_CUR :
|
||||||
|
fseek_origin = SEEK_CUR;
|
||||||
|
break;
|
||||||
|
case ZLIB_FILEFUNC_SEEK_END :
|
||||||
|
fseek_origin = SEEK_END;
|
||||||
|
break;
|
||||||
|
case ZLIB_FILEFUNC_SEEK_SET :
|
||||||
|
fseek_origin = SEEK_SET;
|
||||||
|
break;
|
||||||
|
default: return -1;
|
||||||
|
}
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
if(fseeko64((FILE *)stream, offset, fseek_origin) != 0)
|
||||||
|
ret = -1;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int ZCALLBACK fclose_file_func (voidpf opaque, voidpf stream)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
ret = fclose((FILE *)stream);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ZCALLBACK ferror_file_func (voidpf opaque, voidpf stream)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
ret = ferror((FILE *)stream);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fill_fopen_filefunc (pzlib_filefunc_def)
|
||||||
|
zlib_filefunc_def* pzlib_filefunc_def;
|
||||||
|
{
|
||||||
|
pzlib_filefunc_def->zopen_file = fopen_file_func;
|
||||||
|
pzlib_filefunc_def->zread_file = fread_file_func;
|
||||||
|
pzlib_filefunc_def->zwrite_file = fwrite_file_func;
|
||||||
|
pzlib_filefunc_def->ztell_file = ftell_file_func;
|
||||||
|
pzlib_filefunc_def->zseek_file = fseek_file_func;
|
||||||
|
pzlib_filefunc_def->zclose_file = fclose_file_func;
|
||||||
|
pzlib_filefunc_def->zerror_file = ferror_file_func;
|
||||||
|
pzlib_filefunc_def->opaque = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fill_fopen64_filefunc (zlib_filefunc64_def* pzlib_filefunc_def)
|
||||||
|
{
|
||||||
|
pzlib_filefunc_def->zopen64_file = fopen64_file_func;
|
||||||
|
pzlib_filefunc_def->zread_file = fread_file_func;
|
||||||
|
pzlib_filefunc_def->zwrite_file = fwrite_file_func;
|
||||||
|
pzlib_filefunc_def->ztell64_file = ftell64_file_func;
|
||||||
|
pzlib_filefunc_def->zseek64_file = fseek64_file_func;
|
||||||
|
pzlib_filefunc_def->zclose_file = fclose_file_func;
|
||||||
|
pzlib_filefunc_def->zerror_file = ferror_file_func;
|
||||||
|
pzlib_filefunc_def->opaque = NULL;
|
||||||
|
}
|
200
Pkgs/ioapi.h
Normal file
200
Pkgs/ioapi.h
Normal file
|
@ -0,0 +1,200 @@
|
||||||
|
/* ioapi.h -- IO base function header for compress/uncompress .zip
|
||||||
|
part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )
|
||||||
|
|
||||||
|
Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )
|
||||||
|
|
||||||
|
Modifications for Zip64 support
|
||||||
|
Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )
|
||||||
|
|
||||||
|
For more info read MiniZip_info.txt
|
||||||
|
|
||||||
|
Changes
|
||||||
|
|
||||||
|
Oct-2009 - Defined ZPOS64_T to fpos_t on windows and u_int64_t on linux. (might need to find a better why for this)
|
||||||
|
Oct-2009 - Change to fseeko64, ftello64 and fopen64 so large files would work on linux.
|
||||||
|
More if/def section may be needed to support other platforms
|
||||||
|
Oct-2009 - Defined fxxxx64 calls to normal fopen/ftell/fseek so they would compile on windows.
|
||||||
|
(but you should use iowin32.c for windows instead)
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ZLIBIOAPI64_H
|
||||||
|
#define _ZLIBIOAPI64_H
|
||||||
|
|
||||||
|
#if (!defined(_WIN32)) && (!defined(WIN32))
|
||||||
|
|
||||||
|
// Linux needs this to support file operation on files larger then 4+GB
|
||||||
|
// But might need better if/def to select just the platforms that needs them.
|
||||||
|
|
||||||
|
#ifndef __USE_FILE_OFFSET64
|
||||||
|
#define __USE_FILE_OFFSET64
|
||||||
|
#endif
|
||||||
|
#ifndef __USE_LARGEFILE64
|
||||||
|
#define __USE_LARGEFILE64
|
||||||
|
#endif
|
||||||
|
#ifndef _LARGEFILE64_SOURCE
|
||||||
|
#define _LARGEFILE64_SOURCE
|
||||||
|
#endif
|
||||||
|
#ifndef _FILE_OFFSET_BIT
|
||||||
|
#define _FILE_OFFSET_BIT 64
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "zlib.h"
|
||||||
|
|
||||||
|
#if defined(USE_FILE32API)
|
||||||
|
#define fopen64 fopen
|
||||||
|
#define ftello64 ftell
|
||||||
|
#define fseeko64 fseek
|
||||||
|
#else
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define fopen64 fopen
|
||||||
|
#if (_MSC_VER >= 1400) && (!(defined(NO_MSCVER_FILE64_FUNC)))
|
||||||
|
#define ftello64 _ftelli64
|
||||||
|
#define fseeko64 _fseeki64
|
||||||
|
#else // old MSC
|
||||||
|
#define ftello64 ftell
|
||||||
|
#define fseeko64 fseek
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
#ifndef ZPOS64_T
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define ZPOS64_T fpos_t
|
||||||
|
#else
|
||||||
|
#include <stdint.h>
|
||||||
|
#define ZPOS64_T uint64_t
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_MINIZIP64_CONF_H
|
||||||
|
#include "mz64conf.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* a type choosen by DEFINE */
|
||||||
|
#ifdef HAVE_64BIT_INT_CUSTOM
|
||||||
|
typedef 64BIT_INT_CUSTOM_TYPE ZPOS64_T;
|
||||||
|
#else
|
||||||
|
#ifdef HAS_STDINT_H
|
||||||
|
#include "stdint.h"
|
||||||
|
typedef uint64_t ZPOS64_T;
|
||||||
|
#else
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) || defined(__BORLANDC__)
|
||||||
|
typedef unsigned __int64 ZPOS64_T;
|
||||||
|
#else
|
||||||
|
typedef unsigned long long int ZPOS64_T;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#define ZLIB_FILEFUNC_SEEK_CUR (1)
|
||||||
|
#define ZLIB_FILEFUNC_SEEK_END (2)
|
||||||
|
#define ZLIB_FILEFUNC_SEEK_SET (0)
|
||||||
|
|
||||||
|
#define ZLIB_FILEFUNC_MODE_READ (1)
|
||||||
|
#define ZLIB_FILEFUNC_MODE_WRITE (2)
|
||||||
|
#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3)
|
||||||
|
|
||||||
|
#define ZLIB_FILEFUNC_MODE_EXISTING (4)
|
||||||
|
#define ZLIB_FILEFUNC_MODE_CREATE (8)
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef ZCALLBACK
|
||||||
|
#if (defined(WIN32) || defined(_WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK)
|
||||||
|
#define ZCALLBACK CALLBACK
|
||||||
|
#else
|
||||||
|
#define ZCALLBACK
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode));
|
||||||
|
typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size));
|
||||||
|
typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size));
|
||||||
|
typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream));
|
||||||
|
typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream));
|
||||||
|
|
||||||
|
typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream));
|
||||||
|
typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin));
|
||||||
|
|
||||||
|
|
||||||
|
/* here is the "old" 32 bits structure structure */
|
||||||
|
typedef struct zlib_filefunc_def_s
|
||||||
|
{
|
||||||
|
open_file_func zopen_file;
|
||||||
|
read_file_func zread_file;
|
||||||
|
write_file_func zwrite_file;
|
||||||
|
tell_file_func ztell_file;
|
||||||
|
seek_file_func zseek_file;
|
||||||
|
close_file_func zclose_file;
|
||||||
|
testerror_file_func zerror_file;
|
||||||
|
voidpf opaque;
|
||||||
|
} zlib_filefunc_def;
|
||||||
|
|
||||||
|
typedef ZPOS64_T (ZCALLBACK *tell64_file_func) OF((voidpf opaque, voidpf stream));
|
||||||
|
typedef long (ZCALLBACK *seek64_file_func) OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin));
|
||||||
|
typedef voidpf (ZCALLBACK *open64_file_func) OF((voidpf opaque, const void* filename, int mode));
|
||||||
|
|
||||||
|
typedef struct zlib_filefunc64_def_s
|
||||||
|
{
|
||||||
|
open64_file_func zopen64_file;
|
||||||
|
read_file_func zread_file;
|
||||||
|
write_file_func zwrite_file;
|
||||||
|
tell64_file_func ztell64_file;
|
||||||
|
seek64_file_func zseek64_file;
|
||||||
|
close_file_func zclose_file;
|
||||||
|
testerror_file_func zerror_file;
|
||||||
|
voidpf opaque;
|
||||||
|
} zlib_filefunc64_def;
|
||||||
|
|
||||||
|
void fill_fopen64_filefunc OF((zlib_filefunc64_def* pzlib_filefunc_def));
|
||||||
|
void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def));
|
||||||
|
|
||||||
|
/* now internal definition, only for zip.c and unzip.h */
|
||||||
|
typedef struct zlib_filefunc64_32_def_s
|
||||||
|
{
|
||||||
|
zlib_filefunc64_def zfile_func64;
|
||||||
|
open_file_func zopen32_file;
|
||||||
|
tell_file_func ztell32_file;
|
||||||
|
seek_file_func zseek32_file;
|
||||||
|
} zlib_filefunc64_32_def;
|
||||||
|
|
||||||
|
|
||||||
|
#define ZREAD64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zread_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size))
|
||||||
|
#define ZWRITE64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zwrite_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size))
|
||||||
|
//#define ZTELL64(filefunc,filestream) ((*((filefunc).ztell64_file)) ((filefunc).opaque,filestream))
|
||||||
|
//#define ZSEEK64(filefunc,filestream,pos,mode) ((*((filefunc).zseek64_file)) ((filefunc).opaque,filestream,pos,mode))
|
||||||
|
#define ZCLOSE64(filefunc,filestream) ((*((filefunc).zfile_func64.zclose_file)) ((filefunc).zfile_func64.opaque,filestream))
|
||||||
|
#define ZERROR64(filefunc,filestream) ((*((filefunc).zfile_func64.zerror_file)) ((filefunc).zfile_func64.opaque,filestream))
|
||||||
|
|
||||||
|
voidpf call_zopen64 OF((const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode));
|
||||||
|
long call_zseek64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin));
|
||||||
|
ZPOS64_T call_ztell64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream));
|
||||||
|
|
||||||
|
void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32);
|
||||||
|
|
||||||
|
#define ZOPEN64(filefunc,filename,mode) (call_zopen64((&(filefunc)),(filename),(mode)))
|
||||||
|
#define ZTELL64(filefunc,filestream) (call_ztell64((&(filefunc)),(filestream)))
|
||||||
|
#define ZSEEK64(filefunc,filestream,pos,mode) (call_zseek64((&(filefunc)),(filestream),(pos),(mode)))
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
2125
Pkgs/unzip.c
Normal file
2125
Pkgs/unzip.c
Normal file
File diff suppressed because it is too large
Load diff
437
Pkgs/unzip.h
Normal file
437
Pkgs/unzip.h
Normal file
|
@ -0,0 +1,437 @@
|
||||||
|
/* unzip.h -- IO for uncompress .zip files using zlib
|
||||||
|
Version 1.1, February 14h, 2010
|
||||||
|
part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )
|
||||||
|
|
||||||
|
Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )
|
||||||
|
|
||||||
|
Modifications of Unzip for Zip64
|
||||||
|
Copyright (C) 2007-2008 Even Rouault
|
||||||
|
|
||||||
|
Modifications for Zip64 support on both zip and unzip
|
||||||
|
Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )
|
||||||
|
|
||||||
|
For more info read MiniZip_info.txt
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Condition of use and distribution are the same than zlib :
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express or implied
|
||||||
|
warranty. In no event will the authors be held liable for any damages
|
||||||
|
arising from the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any purpose,
|
||||||
|
including commercial applications, and to alter it and redistribute it
|
||||||
|
freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this software must not be misrepresented; you must not
|
||||||
|
claim that you wrote the original software. If you use this software
|
||||||
|
in a product, an acknowledgment in the product documentation would be
|
||||||
|
appreciated but is not required.
|
||||||
|
2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
misrepresented as being the original software.
|
||||||
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
|
|
||||||
|
---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Changes
|
||||||
|
|
||||||
|
See header of unzip64.c
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _unz64_H
|
||||||
|
#define _unz64_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _ZLIB_H
|
||||||
|
#include "zlib.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _ZLIBIOAPI_H
|
||||||
|
#include "ioapi.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_BZIP2
|
||||||
|
#include "bzlib.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define Z_BZIP2ED 12
|
||||||
|
|
||||||
|
#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP)
|
||||||
|
/* like the STRICT of WIN32, we define a pointer that cannot be converted
|
||||||
|
from (void*) without cast */
|
||||||
|
typedef struct TagunzFile__ { int unused; } unzFile__;
|
||||||
|
typedef unzFile__ *unzFile;
|
||||||
|
#else
|
||||||
|
typedef voidp unzFile;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#define UNZ_OK (0)
|
||||||
|
#define UNZ_END_OF_LIST_OF_FILE (-100)
|
||||||
|
#define UNZ_ERRNO (Z_ERRNO)
|
||||||
|
#define UNZ_EOF (0)
|
||||||
|
#define UNZ_PARAMERROR (-102)
|
||||||
|
#define UNZ_BADZIPFILE (-103)
|
||||||
|
#define UNZ_INTERNALERROR (-104)
|
||||||
|
#define UNZ_CRCERROR (-105)
|
||||||
|
|
||||||
|
/* tm_unz contain date/time info */
|
||||||
|
typedef struct tm_unz_s
|
||||||
|
{
|
||||||
|
uInt tm_sec; /* seconds after the minute - [0,59] */
|
||||||
|
uInt tm_min; /* minutes after the hour - [0,59] */
|
||||||
|
uInt tm_hour; /* hours since midnight - [0,23] */
|
||||||
|
uInt tm_mday; /* day of the month - [1,31] */
|
||||||
|
uInt tm_mon; /* months since January - [0,11] */
|
||||||
|
uInt tm_year; /* years - [1980..2044] */
|
||||||
|
} tm_unz;
|
||||||
|
|
||||||
|
/* unz_global_info structure contain global data about the ZIPfile
|
||||||
|
These data comes from the end of central dir */
|
||||||
|
typedef struct unz_global_info64_s
|
||||||
|
{
|
||||||
|
ZPOS64_T number_entry; /* total number of entries in
|
||||||
|
the central dir on this disk */
|
||||||
|
uLong size_comment; /* size of the global comment of the zipfile */
|
||||||
|
} unz_global_info64;
|
||||||
|
|
||||||
|
typedef struct unz_global_info_s
|
||||||
|
{
|
||||||
|
uLong number_entry; /* total number of entries in
|
||||||
|
the central dir on this disk */
|
||||||
|
uLong size_comment; /* size of the global comment of the zipfile */
|
||||||
|
} unz_global_info;
|
||||||
|
|
||||||
|
/* unz_file_info contain information about a file in the zipfile */
|
||||||
|
typedef struct unz_file_info64_s
|
||||||
|
{
|
||||||
|
uLong version; /* version made by 2 bytes */
|
||||||
|
uLong version_needed; /* version needed to extract 2 bytes */
|
||||||
|
uLong flag; /* general purpose bit flag 2 bytes */
|
||||||
|
uLong compression_method; /* compression method 2 bytes */
|
||||||
|
uLong dosDate; /* last mod file date in Dos fmt 4 bytes */
|
||||||
|
uLong crc; /* crc-32 4 bytes */
|
||||||
|
ZPOS64_T compressed_size; /* compressed size 8 bytes */
|
||||||
|
ZPOS64_T uncompressed_size; /* uncompressed size 8 bytes */
|
||||||
|
uLong size_filename; /* filename length 2 bytes */
|
||||||
|
uLong size_file_extra; /* extra field length 2 bytes */
|
||||||
|
uLong size_file_comment; /* file comment length 2 bytes */
|
||||||
|
|
||||||
|
uLong disk_num_start; /* disk number start 2 bytes */
|
||||||
|
uLong internal_fa; /* internal file attributes 2 bytes */
|
||||||
|
uLong external_fa; /* external file attributes 4 bytes */
|
||||||
|
|
||||||
|
tm_unz tmu_date;
|
||||||
|
} unz_file_info64;
|
||||||
|
|
||||||
|
typedef struct unz_file_info_s
|
||||||
|
{
|
||||||
|
uLong version; /* version made by 2 bytes */
|
||||||
|
uLong version_needed; /* version needed to extract 2 bytes */
|
||||||
|
uLong flag; /* general purpose bit flag 2 bytes */
|
||||||
|
uLong compression_method; /* compression method 2 bytes */
|
||||||
|
uLong dosDate; /* last mod file date in Dos fmt 4 bytes */
|
||||||
|
uLong crc; /* crc-32 4 bytes */
|
||||||
|
uLong compressed_size; /* compressed size 4 bytes */
|
||||||
|
uLong uncompressed_size; /* uncompressed size 4 bytes */
|
||||||
|
uLong size_filename; /* filename length 2 bytes */
|
||||||
|
uLong size_file_extra; /* extra field length 2 bytes */
|
||||||
|
uLong size_file_comment; /* file comment length 2 bytes */
|
||||||
|
|
||||||
|
uLong disk_num_start; /* disk number start 2 bytes */
|
||||||
|
uLong internal_fa; /* internal file attributes 2 bytes */
|
||||||
|
uLong external_fa; /* external file attributes 4 bytes */
|
||||||
|
|
||||||
|
tm_unz tmu_date;
|
||||||
|
} unz_file_info;
|
||||||
|
|
||||||
|
extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1,
|
||||||
|
const char* fileName2,
|
||||||
|
int iCaseSensitivity));
|
||||||
|
/*
|
||||||
|
Compare two filename (fileName1,fileName2).
|
||||||
|
If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp)
|
||||||
|
If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi
|
||||||
|
or strcasecmp)
|
||||||
|
If iCaseSenisivity = 0, case sensitivity is defaut of your operating system
|
||||||
|
(like 1 on Unix, 2 on Windows)
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
extern unzFile ZEXPORT unzOpen OF((const char *path));
|
||||||
|
extern unzFile ZEXPORT unzOpen64 OF((const void *path));
|
||||||
|
/*
|
||||||
|
Open a Zip file. path contain the full pathname (by example,
|
||||||
|
on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer
|
||||||
|
"zlib/zlib113.zip".
|
||||||
|
If the zipfile cannot be opened (file don't exist or in not valid), the
|
||||||
|
return value is NULL.
|
||||||
|
Else, the return value is a unzFile Handle, usable with other function
|
||||||
|
of this unzip package.
|
||||||
|
the "64" function take a const void* pointer, because the path is just the
|
||||||
|
value passed to the open64_file_func callback.
|
||||||
|
Under Windows, if UNICODE is defined, using fill_fopen64_filefunc, the path
|
||||||
|
is a pointer to a wide unicode string (LPCTSTR is LPCWSTR), so const char*
|
||||||
|
does not describe the reality
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
extern unzFile ZEXPORT unzOpen2 OF((const char *path,
|
||||||
|
zlib_filefunc_def* pzlib_filefunc_def));
|
||||||
|
/*
|
||||||
|
Open a Zip file, like unzOpen, but provide a set of file low level API
|
||||||
|
for read/write the zip file (see ioapi.h)
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern unzFile ZEXPORT unzOpen2_64 OF((const void *path,
|
||||||
|
zlib_filefunc64_def* pzlib_filefunc_def));
|
||||||
|
/*
|
||||||
|
Open a Zip file, like unz64Open, but provide a set of file low level API
|
||||||
|
for read/write the zip file (see ioapi.h)
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern int ZEXPORT unzClose OF((unzFile file));
|
||||||
|
/*
|
||||||
|
Close a ZipFile opened with unzipOpen.
|
||||||
|
If there is files inside the .Zip opened with unzOpenCurrentFile (see later),
|
||||||
|
these files MUST be closed with unzipCloseCurrentFile before call unzipClose.
|
||||||
|
return UNZ_OK if there is no problem. */
|
||||||
|
|
||||||
|
extern int ZEXPORT unzGetGlobalInfo OF((unzFile file,
|
||||||
|
unz_global_info *pglobal_info));
|
||||||
|
|
||||||
|
extern int ZEXPORT unzGetGlobalInfo64 OF((unzFile file,
|
||||||
|
unz_global_info64 *pglobal_info));
|
||||||
|
/*
|
||||||
|
Write info about the ZipFile in the *pglobal_info structure.
|
||||||
|
No preparation of the structure is needed
|
||||||
|
return UNZ_OK if there is no problem. */
|
||||||
|
|
||||||
|
|
||||||
|
extern int ZEXPORT unzGetGlobalComment OF((unzFile file,
|
||||||
|
char *szComment,
|
||||||
|
uLong uSizeBuf));
|
||||||
|
/*
|
||||||
|
Get the global comment string of the ZipFile, in the szComment buffer.
|
||||||
|
uSizeBuf is the size of the szComment buffer.
|
||||||
|
return the number of byte copied or an error code <0
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************/
|
||||||
|
/* Unzip package allow you browse the directory of the zipfile */
|
||||||
|
|
||||||
|
extern int ZEXPORT unzGoToFirstFile OF((unzFile file));
|
||||||
|
/*
|
||||||
|
Set the current file of the zipfile to the first file.
|
||||||
|
return UNZ_OK if there is no problem
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern int ZEXPORT unzGoToNextFile OF((unzFile file));
|
||||||
|
/*
|
||||||
|
Set the current file of the zipfile to the next file.
|
||||||
|
return UNZ_OK if there is no problem
|
||||||
|
return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest.
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern int ZEXPORT unzLocateFile OF((unzFile file,
|
||||||
|
const char *szFileName,
|
||||||
|
int iCaseSensitivity));
|
||||||
|
/*
|
||||||
|
Try locate the file szFileName in the zipfile.
|
||||||
|
For the iCaseSensitivity signification, see unzStringFileNameCompare
|
||||||
|
|
||||||
|
return value :
|
||||||
|
UNZ_OK if the file is found. It becomes the current file.
|
||||||
|
UNZ_END_OF_LIST_OF_FILE if the file is not found
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/* ****************************************** */
|
||||||
|
/* Ryan supplied functions */
|
||||||
|
/* unz_file_info contain information about a file in the zipfile */
|
||||||
|
typedef struct unz_file_pos_s
|
||||||
|
{
|
||||||
|
uLong pos_in_zip_directory; /* offset in zip file directory */
|
||||||
|
uLong num_of_file; /* # of file */
|
||||||
|
} unz_file_pos;
|
||||||
|
|
||||||
|
extern int ZEXPORT unzGetFilePos(
|
||||||
|
unzFile file,
|
||||||
|
unz_file_pos* file_pos);
|
||||||
|
|
||||||
|
extern int ZEXPORT unzGoToFilePos(
|
||||||
|
unzFile file,
|
||||||
|
unz_file_pos* file_pos);
|
||||||
|
|
||||||
|
typedef struct unz64_file_pos_s
|
||||||
|
{
|
||||||
|
ZPOS64_T pos_in_zip_directory; /* offset in zip file directory */
|
||||||
|
ZPOS64_T num_of_file; /* # of file */
|
||||||
|
} unz64_file_pos;
|
||||||
|
|
||||||
|
extern int ZEXPORT unzGetFilePos64(
|
||||||
|
unzFile file,
|
||||||
|
unz64_file_pos* file_pos);
|
||||||
|
|
||||||
|
extern int ZEXPORT unzGoToFilePos64(
|
||||||
|
unzFile file,
|
||||||
|
const unz64_file_pos* file_pos);
|
||||||
|
|
||||||
|
/* ****************************************** */
|
||||||
|
|
||||||
|
extern int ZEXPORT unzGetCurrentFileInfo64 OF((unzFile file,
|
||||||
|
unz_file_info64 *pfile_info,
|
||||||
|
char *szFileName,
|
||||||
|
uLong fileNameBufferSize,
|
||||||
|
void *extraField,
|
||||||
|
uLong extraFieldBufferSize,
|
||||||
|
char *szComment,
|
||||||
|
uLong commentBufferSize));
|
||||||
|
|
||||||
|
extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file,
|
||||||
|
unz_file_info *pfile_info,
|
||||||
|
char *szFileName,
|
||||||
|
uLong fileNameBufferSize,
|
||||||
|
void *extraField,
|
||||||
|
uLong extraFieldBufferSize,
|
||||||
|
char *szComment,
|
||||||
|
uLong commentBufferSize));
|
||||||
|
/*
|
||||||
|
Get Info about the current file
|
||||||
|
if pfile_info!=NULL, the *pfile_info structure will contain somes info about
|
||||||
|
the current file
|
||||||
|
if szFileName!=NULL, the filemane string will be copied in szFileName
|
||||||
|
(fileNameBufferSize is the size of the buffer)
|
||||||
|
if extraField!=NULL, the extra field information will be copied in extraField
|
||||||
|
(extraFieldBufferSize is the size of the buffer).
|
||||||
|
This is the Central-header version of the extra field
|
||||||
|
if szComment!=NULL, the comment string of the file will be copied in szComment
|
||||||
|
(commentBufferSize is the size of the buffer)
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/** Addition for GDAL : START */
|
||||||
|
|
||||||
|
extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64 OF((unzFile file));
|
||||||
|
|
||||||
|
/** Addition for GDAL : END */
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************/
|
||||||
|
/* for reading the content of the current zipfile, you can open it, read data
|
||||||
|
from it, and close it (you can close it before reading all the file)
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern int ZEXPORT unzOpenCurrentFile OF((unzFile file));
|
||||||
|
/*
|
||||||
|
Open for reading data the current file in the zipfile.
|
||||||
|
If there is no error, the return value is UNZ_OK.
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file,
|
||||||
|
const char* password));
|
||||||
|
/*
|
||||||
|
Open for reading data the current file in the zipfile.
|
||||||
|
password is a crypting password
|
||||||
|
If there is no error, the return value is UNZ_OK.
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file,
|
||||||
|
int* method,
|
||||||
|
int* level,
|
||||||
|
int raw));
|
||||||
|
/*
|
||||||
|
Same than unzOpenCurrentFile, but open for read raw the file (not uncompress)
|
||||||
|
if raw==1
|
||||||
|
*method will receive method of compression, *level will receive level of
|
||||||
|
compression
|
||||||
|
note : you can set level parameter as NULL (if you did not want known level,
|
||||||
|
but you CANNOT set method parameter as NULL
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file,
|
||||||
|
int* method,
|
||||||
|
int* level,
|
||||||
|
int raw,
|
||||||
|
const char* password));
|
||||||
|
/*
|
||||||
|
Same than unzOpenCurrentFile, but open for read raw the file (not uncompress)
|
||||||
|
if raw==1
|
||||||
|
*method will receive method of compression, *level will receive level of
|
||||||
|
compression
|
||||||
|
note : you can set level parameter as NULL (if you did not want known level,
|
||||||
|
but you CANNOT set method parameter as NULL
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
extern int ZEXPORT unzCloseCurrentFile OF((unzFile file));
|
||||||
|
/*
|
||||||
|
Close the file in zip opened with unzOpenCurrentFile
|
||||||
|
Return UNZ_CRCERROR if all the file was read but the CRC is not good
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern int ZEXPORT unzReadCurrentFile OF((unzFile file,
|
||||||
|
voidp buf,
|
||||||
|
unsigned len));
|
||||||
|
/*
|
||||||
|
Read bytes from the current file (opened by unzOpenCurrentFile)
|
||||||
|
buf contain buffer where data must be copied
|
||||||
|
len the size of buf.
|
||||||
|
|
||||||
|
return the number of byte copied if somes bytes are copied
|
||||||
|
return 0 if the end of file was reached
|
||||||
|
return <0 with error code if there is an error
|
||||||
|
(UNZ_ERRNO for IO error, or zLib error for uncompress error)
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern z_off_t ZEXPORT unztell OF((unzFile file));
|
||||||
|
|
||||||
|
extern ZPOS64_T ZEXPORT unztell64 OF((unzFile file));
|
||||||
|
/*
|
||||||
|
Give the current position in uncompressed data
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern int ZEXPORT unzeof OF((unzFile file));
|
||||||
|
/*
|
||||||
|
return 1 if the end of file was reached, 0 elsewhere
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file,
|
||||||
|
voidp buf,
|
||||||
|
unsigned len));
|
||||||
|
/*
|
||||||
|
Read extra field from the current file (opened by unzOpenCurrentFile)
|
||||||
|
This is the local-header version of the extra field (sometimes, there is
|
||||||
|
more info in the local-header version than in the central-header)
|
||||||
|
|
||||||
|
if buf==NULL, it return the size of the local extra field
|
||||||
|
|
||||||
|
if buf!=NULL, len is the size of the buffer, the extra header is copied in
|
||||||
|
buf.
|
||||||
|
the return value is the number of bytes copied in buf, or (if <0)
|
||||||
|
the error code
|
||||||
|
*/
|
||||||
|
|
||||||
|
/***************************************************************************/
|
||||||
|
|
||||||
|
/* Get the current file offset */
|
||||||
|
extern ZPOS64_T ZEXPORT unzGetOffset64 (unzFile file);
|
||||||
|
extern uLong ZEXPORT unzGetOffset (unzFile file);
|
||||||
|
|
||||||
|
/* Set the current file offset */
|
||||||
|
extern int ZEXPORT unzSetOffset64 (unzFile file, ZPOS64_T pos);
|
||||||
|
extern int ZEXPORT unzSetOffset (unzFile file, uLong pos);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _unz64_H */
|
1287
Pkgs/wglew.h
Normal file
1287
Pkgs/wglew.h
Normal file
File diff suppressed because it is too large
Load diff
332
ROMLoad.cpp
Normal file
332
ROMLoad.cpp
Normal file
|
@ -0,0 +1,332 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ROMLoad.cpp
|
||||||
|
*
|
||||||
|
* ROM loading functions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <new>
|
||||||
|
#include <string.h>
|
||||||
|
#include "Supermodel.h"
|
||||||
|
#include "Pkgs/unzip.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CopyRegion(dest, destOffset, destSize, src, srcSize):
|
||||||
|
*
|
||||||
|
* Repeatedly mirror (copy) to destination from source until destination is
|
||||||
|
* filled.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* dest Destination region.
|
||||||
|
* destOffset Offset within destination to begin mirroring to.
|
||||||
|
* destSize Size in bytes of destination region.
|
||||||
|
* src Source region to copy from.
|
||||||
|
* srcSize Size of region to copy from.
|
||||||
|
*/
|
||||||
|
void CopyRegion(UINT8 *dest, unsigned destOffset, unsigned destSize, UINT8 *src, unsigned srcSize)
|
||||||
|
{
|
||||||
|
while (destOffset < destSize)
|
||||||
|
{
|
||||||
|
// If we'll overrun the destination, trim the copy length
|
||||||
|
if ((destOffset+srcSize) >= destSize)
|
||||||
|
srcSize = destSize-destOffset;
|
||||||
|
|
||||||
|
// Copy!
|
||||||
|
memcpy(&dest[destOffset], src, srcSize);
|
||||||
|
|
||||||
|
destOffset += srcSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search for a ROM within a single game based on its CRC
|
||||||
|
static BOOL FindROMByCRCInGame(const struct GameInfo **gamePtr, int *romIdxPtr, const struct GameInfo *Game, UINT32 crc)
|
||||||
|
{
|
||||||
|
unsigned j;
|
||||||
|
|
||||||
|
for (j = 0; Game->ROM[j].region != NULL; j++)
|
||||||
|
{
|
||||||
|
if (crc == Game->ROM[j].crc) // found it!
|
||||||
|
{
|
||||||
|
*gamePtr = Game;
|
||||||
|
*romIdxPtr = j;
|
||||||
|
return OKAY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search for a ROM in the complete game list based on CRC32 and return its GameInfo and ROMInfo entries
|
||||||
|
static BOOL FindROMByCRC(const struct GameInfo **gamePtr, int *romIdxPtr, const struct GameInfo *GameList, const struct GameInfo *TryGame, UINT32 crc)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
if (TryGame != NULL)
|
||||||
|
{
|
||||||
|
if (FindROMByCRCInGame(gamePtr, romIdxPtr, TryGame, crc) == OKAY)
|
||||||
|
return OKAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; GameList[i].title != NULL; i++)
|
||||||
|
{
|
||||||
|
if (FindROMByCRCInGame(gamePtr, romIdxPtr, &(GameList[i]), crc) == OKAY)
|
||||||
|
return OKAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ByteSwap(UINT8 *buf, unsigned size)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
UINT8 x;
|
||||||
|
|
||||||
|
for (i = 0; i < size; i += 2)
|
||||||
|
{
|
||||||
|
x = buf[i+0];
|
||||||
|
buf[i+0] = buf[i+1];
|
||||||
|
buf[i+1] = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load a single ROM file
|
||||||
|
static BOOL LoadROM(UINT8 *buf, unsigned bufSize, const struct ROMMap *Map, const struct ROMInfo *ROM, unzFile zf, const char *zipFile, BOOL loadAll)
|
||||||
|
{
|
||||||
|
char file[2048+1];
|
||||||
|
int err, bytes;
|
||||||
|
unz_file_info fileInfo;
|
||||||
|
unsigned i, j, destIdx, srcIdx;
|
||||||
|
|
||||||
|
// Read the file into the buffer
|
||||||
|
err = unzGetCurrentFileInfo(zf, &fileInfo, file, 2048, NULL, 0, NULL, 0);
|
||||||
|
if (err != UNZ_OK)
|
||||||
|
return ErrorLog("Unable to extract a file name from %s.", zipFile);
|
||||||
|
if (fileInfo.uncompressed_size != ROM->fileSize)
|
||||||
|
return ErrorLog("%s in %s is not the correct size (must be %d bytes).", file, zipFile, ROM->fileSize);
|
||||||
|
err = unzOpenCurrentFile(zf);
|
||||||
|
if (UNZ_OK != err)
|
||||||
|
return ErrorLog("Unable to read %s from %s.", file, zipFile);
|
||||||
|
bytes = unzReadCurrentFile(zf, buf, bufSize);
|
||||||
|
if (bytes != ROM->fileSize)
|
||||||
|
{
|
||||||
|
unzCloseCurrentFile(zf);
|
||||||
|
return ErrorLog("Unable to read %s from %s.", file, zipFile);
|
||||||
|
}
|
||||||
|
err = unzCloseCurrentFile(zf);
|
||||||
|
if (UNZ_CRCERROR == err)
|
||||||
|
ErrorLog("CRC error reading %s from %s. File may be corrupt.", file, zipFile);
|
||||||
|
|
||||||
|
// Byte swap
|
||||||
|
if (ROM->byteSwap)
|
||||||
|
ByteSwap(buf, ROM->fileSize);
|
||||||
|
|
||||||
|
// Find out how to map the ROM and do it
|
||||||
|
for (i = 0; Map[i].region != NULL; i++)
|
||||||
|
{
|
||||||
|
if (!strcmp(Map[i].region, ROM->region))
|
||||||
|
{
|
||||||
|
destIdx = ROM->offset;
|
||||||
|
for (srcIdx = 0; srcIdx < ROM->fileSize; )
|
||||||
|
{
|
||||||
|
for (j = 0; j < ROM->groupSize; j++)
|
||||||
|
Map[i].ptr[destIdx+j] = buf[srcIdx++];
|
||||||
|
|
||||||
|
destIdx += ROM->stride;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OKAY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loadAll) // need to load all ROMs, so there should be no unmapped regions
|
||||||
|
return ErrorLog("%s:%d: No mapping for \"%s\".", __FILE__, __LINE__, ROM->region);
|
||||||
|
else
|
||||||
|
return OKAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LoadROMSetFromZIPFile(Map, GameList, zipFile):
|
||||||
|
*
|
||||||
|
* Loads a complete ROM set from a ZIP archive. Automatically detects the game.
|
||||||
|
* If multiple games exist within the archive, an error will be printed and all
|
||||||
|
* but the first detected game will be ignored.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* Map A list of pointers to the memory buffers for each ROM
|
||||||
|
* region.
|
||||||
|
* GameList List of all supported games and their ROMs.
|
||||||
|
* zipFile ZIP file to load from.
|
||||||
|
* loadAll If true, will check to ensure all ROMs were loaded.
|
||||||
|
* Otherwise, omits this check and loads only specified
|
||||||
|
* regions.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Pointer to GameInfo struct for loaded game if successful, NULL
|
||||||
|
* otherwise. Prints errors.
|
||||||
|
*/
|
||||||
|
const struct GameInfo * LoadROMSetFromZIPFile(const struct ROMMap *Map, const struct GameInfo *GameList, const char *zipFile, BOOL loadAll)
|
||||||
|
{
|
||||||
|
unzFile zf;
|
||||||
|
unz_file_info fileInfo;
|
||||||
|
const struct GameInfo *Game = NULL, *CurGame;
|
||||||
|
int romIdx; // index within Game->ROM
|
||||||
|
unsigned romsFound[sizeof(Game->ROM)/sizeof(struct ROMInfo)], numROMs;
|
||||||
|
int err;
|
||||||
|
unsigned i, n, maxSize;
|
||||||
|
BOOL multipleGameError = FALSE;
|
||||||
|
UINT8 *buf;
|
||||||
|
|
||||||
|
// Try to open file
|
||||||
|
zf = unzOpen(zipFile);
|
||||||
|
if (NULL == zf)
|
||||||
|
{
|
||||||
|
ErrorLog("Unable to open %s.", zipFile);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check ROMs: scan ZIP file for first known ROM and check to ensure all ROMs are present
|
||||||
|
memset(romsFound, 0, sizeof(romsFound));
|
||||||
|
err = unzGoToFirstFile(zf);
|
||||||
|
if (UNZ_OK != err)
|
||||||
|
{
|
||||||
|
ErrorLog("Unable to read the contents of %s (code %X)", zipFile, err);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
for (; err != UNZ_END_OF_LIST_OF_FILE; err = unzGoToNextFile(zf))
|
||||||
|
{
|
||||||
|
// Identify the file we're looking at
|
||||||
|
err = unzGetCurrentFileInfo(zf, &fileInfo, NULL, 0, NULL, 0, NULL, 0);
|
||||||
|
if (err != UNZ_OK)
|
||||||
|
continue;
|
||||||
|
if (OKAY != FindROMByCRC(&CurGame, &romIdx, GameList, Game, fileInfo.crc))
|
||||||
|
continue;
|
||||||
|
if (Game == NULL) // this is the first game we've identified within the ZIP
|
||||||
|
{
|
||||||
|
Game = CurGame;
|
||||||
|
DebugLog("%ROM set identified: %s (%s), %s\n", Game->id, Game->title, zipFile);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (CurGame != Game)
|
||||||
|
{
|
||||||
|
DebugLog("%s also contains: %s (%s)\n", zipFile, CurGame->id, CurGame->title);
|
||||||
|
if (multipleGameError == FALSE) // only warn about this once
|
||||||
|
{
|
||||||
|
ErrorLog("Multiple games were found in %s; loading \"%s\".", zipFile, Game->title);
|
||||||
|
multipleGameError = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have found a ROM for the correct game, mark it
|
||||||
|
if (Game == CurGame)
|
||||||
|
romsFound[romIdx] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Game == NULL)
|
||||||
|
{
|
||||||
|
ErrorLog("%s contains no supported games.", zipFile);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute how many ROM files this game has
|
||||||
|
for (numROMs = 0; Game->ROM[numROMs].region != NULL; numROMs++)
|
||||||
|
;
|
||||||
|
|
||||||
|
// If not all ROMs were present, tell the user
|
||||||
|
err = OKAY;
|
||||||
|
for (i = 0; i < numROMs; i++)
|
||||||
|
{
|
||||||
|
if (romsFound[i] == 0)
|
||||||
|
err |= ErrorLog("%s (CRC=%08X) is missing from %s.", Game->ROM[i].file, Game->ROM[i].crc, zipFile);
|
||||||
|
}
|
||||||
|
if (err != OKAY)
|
||||||
|
{
|
||||||
|
unzClose(zf);
|
||||||
|
return NULL;
|
||||||
|
//return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate memory for the largest ROM to load
|
||||||
|
maxSize = 0;
|
||||||
|
for (i = 0; i < numROMs; i++)
|
||||||
|
{
|
||||||
|
if (Game->ROM[i].fileSize > maxSize)
|
||||||
|
maxSize = Game->ROM[i].fileSize;
|
||||||
|
}
|
||||||
|
buf = new(std::nothrow) UINT8[maxSize];
|
||||||
|
if (NULL == buf)
|
||||||
|
{
|
||||||
|
unzClose(zf);
|
||||||
|
ErrorLog("Insufficient memory to load ROM files (%d bytes).", maxSize);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load ROMs
|
||||||
|
memset(romsFound, 0, sizeof(romsFound));
|
||||||
|
err = unzGoToFirstFile(zf);
|
||||||
|
if (UNZ_OK != err)
|
||||||
|
{
|
||||||
|
ErrorLog("Unable to read the contents of %s (code %X).", zipFile, err);
|
||||||
|
err = FAIL;
|
||||||
|
goto Quit;
|
||||||
|
}
|
||||||
|
for (; err != UNZ_END_OF_LIST_OF_FILE; err = unzGoToNextFile(zf))
|
||||||
|
{
|
||||||
|
err = unzGetCurrentFileInfo(zf, &fileInfo, NULL, 0, NULL, 0, NULL, 0);
|
||||||
|
if (err != UNZ_OK)
|
||||||
|
continue;
|
||||||
|
if (OKAY != FindROMByCRC(&CurGame, &romIdx, GameList, Game, fileInfo.crc))
|
||||||
|
continue;
|
||||||
|
if (CurGame == Game) // if ROM belongs to correct game
|
||||||
|
{
|
||||||
|
if (OKAY == LoadROM(buf, maxSize, Map, &Game->ROM[romIdx], zf, zipFile, loadAll))
|
||||||
|
romsFound[romIdx] = 1; // success! mark as loaded
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure all ROMs were loaded
|
||||||
|
if (loadAll)
|
||||||
|
{
|
||||||
|
n = 0;
|
||||||
|
for (i = 0; i < numROMs; i++)
|
||||||
|
{
|
||||||
|
if (romsFound[i])
|
||||||
|
++n;
|
||||||
|
else
|
||||||
|
ErrorLog("Failed to load %s (CRC=%08X) from %s.", Game->ROM[i].file, Game->ROM[i].crc, zipFile);
|
||||||
|
}
|
||||||
|
if (n < numROMs)
|
||||||
|
err = FAIL;
|
||||||
|
else
|
||||||
|
err = OKAY;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
err = OKAY;
|
||||||
|
|
||||||
|
Quit:
|
||||||
|
unzClose(zf);
|
||||||
|
delete [] buf;
|
||||||
|
return (err == OKAY) ? Game : NULL;
|
||||||
|
}
|
118
ROMLoad.h
Normal file
118
ROMLoad.h
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ROMLoad.h
|
||||||
|
*
|
||||||
|
* Header file for ROM loading functions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDED_ROMLOAD_H
|
||||||
|
#define INCLUDED_ROMLOAD_H
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Data Structures
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ROMInfo:
|
||||||
|
*
|
||||||
|
* Describes a single ROM file.
|
||||||
|
*/
|
||||||
|
struct ROMInfo
|
||||||
|
{
|
||||||
|
// Function
|
||||||
|
const char *region; // ROM region identifier (used as a key to search ROMMap)
|
||||||
|
|
||||||
|
// Information used to identify files
|
||||||
|
const char *file; // file name
|
||||||
|
UINT32 crc; // CRC-32 checksum (same as zip format)
|
||||||
|
unsigned fileSize; // file size in bytes (must be the same as all other ROMs with same region ID)
|
||||||
|
|
||||||
|
// Interleaving information
|
||||||
|
unsigned groupSize; // number of consecutive bytes to fetch each time (groupSize%2 must = 0, must be consistent for region)
|
||||||
|
unsigned offset; // starting offset within ROM region
|
||||||
|
unsigned stride; // number of bytes to skip before loading next group of bytes from file (must be >= groupSize)
|
||||||
|
BOOL byteSwap; // swap every pair of bytes if true
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ROMMap:
|
||||||
|
*
|
||||||
|
* Describes how to map ROM regions (where to load them). This structure is
|
||||||
|
* assembled after memory allocation is completed and tells the ROM loading
|
||||||
|
* functions where to load files by matching region identifiers with memory
|
||||||
|
* pointers.
|
||||||
|
*
|
||||||
|
* Both pointers must be set to NULL to terminate an array of ROM maps.
|
||||||
|
*/
|
||||||
|
struct ROMMap
|
||||||
|
{
|
||||||
|
const char *region; // ROM region identifier
|
||||||
|
UINT8 *ptr; // pointer to memory region
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Functions
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CopyRegion(dest, destOffset, destSize, src, srcSize):
|
||||||
|
*
|
||||||
|
* Repeatedly mirror (copy) to destination from source until destination is
|
||||||
|
* filled.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* dest Destination region.
|
||||||
|
* destOffset Offset within destination to begin mirroring to.
|
||||||
|
* destSize Size in bytes of destination region.
|
||||||
|
* src Source region to copy from.
|
||||||
|
* srcSize Size of region to copy from.
|
||||||
|
*/
|
||||||
|
extern void CopyRegion(UINT8 *dest, unsigned destOffset, unsigned destSize, UINT8 *src, unsigned srcSize);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LoadROMSetFromZIPFile(Map, GameList, zipFile):
|
||||||
|
*
|
||||||
|
* Loads a complete ROM set from a zip archive. Automatically detects the game.
|
||||||
|
* If multiple games exist within the archive, an error will be printed and all
|
||||||
|
* but the first detected game will be ignored.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* Map A list of pointers to the memory buffers for each ROM
|
||||||
|
* region.
|
||||||
|
* GameList List of all supported games and their ROMs.
|
||||||
|
* zipFile ZIP file to load from.
|
||||||
|
* loadAll If true, will check to ensure all ROMs were loaded.
|
||||||
|
* Otherwise, omits this check and loads only specified
|
||||||
|
* regions.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Pointer to GameInfo struct for loaded game if successful, NULL
|
||||||
|
* otherwise. Prints errors.
|
||||||
|
*/
|
||||||
|
extern const struct GameInfo * LoadROMSetFromZIPFile(const struct ROMMap *Map, const struct GameInfo *GameList, const char *zipFile,
|
||||||
|
BOOL loadAll);
|
||||||
|
|
||||||
|
|
||||||
|
#endif // INCLUDED_ROMLOAD_H
|
2021
Sound/SCSP.cpp
Normal file
2021
Sound/SCSP.cpp
Normal file
File diff suppressed because it is too large
Load diff
71
Sound/SCSP.h
Normal file
71
Sound/SCSP.h
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SCSP.h
|
||||||
|
*
|
||||||
|
* Header file defining for SCSP emulation.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDED_SCSP_H
|
||||||
|
#define INCLUDED_SCSP_H
|
||||||
|
|
||||||
|
|
||||||
|
void SCSP_w8(unsigned int addr,unsigned char val);
|
||||||
|
void SCSP_w16(unsigned int addr,unsigned short val);
|
||||||
|
void SCSP_w32(unsigned int addr,unsigned int val);
|
||||||
|
unsigned char SCSP_r8(unsigned int addr);
|
||||||
|
unsigned short SCSP_r16(unsigned int addr);
|
||||||
|
unsigned int SCSP_r32(unsigned int addr);
|
||||||
|
|
||||||
|
void SCSP_SetCB(int (*Run68k)(int cycles),void (*Int68k)(int irq), CIRQ *ppcIRQObjectPtr, unsigned soundIRQBit);
|
||||||
|
void SCSP_Update();
|
||||||
|
void SCSP_MidiIn(unsigned char);
|
||||||
|
void SCSP_MidiOutW(unsigned char);
|
||||||
|
unsigned char SCSP_MidiOutFill();
|
||||||
|
unsigned char SCSP_MidiInFill();
|
||||||
|
void SCSP_CpuRunScanline();
|
||||||
|
unsigned char SCSP_MidiOutR();
|
||||||
|
void SCSP_Init(int n);
|
||||||
|
void SCSP_SetRAM(int n,unsigned char *r);
|
||||||
|
void SCSP_RTECheck();
|
||||||
|
int SCSP_IRQCB(int);
|
||||||
|
|
||||||
|
void SCSP_Master_w8(unsigned int addr,unsigned char val);
|
||||||
|
void SCSP_Master_w16(unsigned int addr,unsigned short val);
|
||||||
|
void SCSP_Master_w32(unsigned int addr,unsigned int val);
|
||||||
|
void SCSP_Slave_w8(unsigned int addr,unsigned char val);
|
||||||
|
void SCSP_Slave_w16(unsigned int addr,unsigned short val);
|
||||||
|
void SCSP_Slave_w32(unsigned int addr,unsigned int val);
|
||||||
|
unsigned char SCSP_Master_r8(unsigned int addr);
|
||||||
|
unsigned short SCSP_Master_r16(unsigned int addr);
|
||||||
|
unsigned int SCSP_Master_r32(unsigned int addr);
|
||||||
|
unsigned char SCSP_Slave_r8(unsigned int addr);
|
||||||
|
unsigned short SCSP_Slave_r16(unsigned int addr);
|
||||||
|
unsigned int SCSP_Slave_r32(unsigned int addr);
|
||||||
|
|
||||||
|
// Supermodel interface functions
|
||||||
|
void SCSP_WriteMIDICtrlPort(UINT8 data);
|
||||||
|
void SCSP_SetBuffers(INT16 *leftBufferPtr, INT16 *rightBufferPtr, int bufferLength);
|
||||||
|
void SCSP_Deinit(void);
|
||||||
|
|
||||||
|
|
||||||
|
#endif // INCLUDED_SCSP_H
|
1101
Sound/SCSPDSP.cpp
Normal file
1101
Sound/SCSPDSP.cpp
Normal file
File diff suppressed because it is too large
Load diff
59
Sound/SCSPDSP.h
Normal file
59
Sound/SCSPDSP.h
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
//#ifndef SCSPDSP_H
|
||||||
|
#define SCSPDSP_H
|
||||||
|
|
||||||
|
//#define DYNDSP
|
||||||
|
#define DYNOPT 1 //set to 1 to enable optimization of recompiler
|
||||||
|
|
||||||
|
|
||||||
|
//the DSP Context
|
||||||
|
struct _SCSPDSP
|
||||||
|
{
|
||||||
|
//Config
|
||||||
|
unsigned short *SCSPRAM;
|
||||||
|
unsigned int RBP; //Ring buf pointer
|
||||||
|
unsigned int RBL; //Delay ram (Ring buffer) size in words
|
||||||
|
|
||||||
|
//context
|
||||||
|
|
||||||
|
signed short COEF[64]; //16 bit signed
|
||||||
|
unsigned short MADRS[32]; //offsets (in words), 16 bit
|
||||||
|
unsigned short MPRO[128*4]; //128 steps 64 bit
|
||||||
|
signed int TEMP[128]; //TEMP regs,24 bit signed
|
||||||
|
signed int MEMS[32]; //MEMS regs,24 bit signed
|
||||||
|
unsigned int DEC;
|
||||||
|
|
||||||
|
//input
|
||||||
|
signed int MIXS[16]; //MIXS, 24 bit signed
|
||||||
|
signed short EXTS[2]; //External inputs (CDDA) 16 bit signed
|
||||||
|
|
||||||
|
//output
|
||||||
|
signed short EFREG[16]; //EFREG, 16 bit signed
|
||||||
|
|
||||||
|
bool Stopped;
|
||||||
|
int LastStep;
|
||||||
|
#ifdef DYNDSP
|
||||||
|
signed int ACC; //26 bit
|
||||||
|
signed int SHIFTED; //24 bit
|
||||||
|
signed int X; //24 bit
|
||||||
|
signed int Y; //13 bit
|
||||||
|
signed int B; //26 bit
|
||||||
|
signed int INPUTS; //24 bit
|
||||||
|
signed int MEMVAL;
|
||||||
|
signed int FRC_REG; //13 bit
|
||||||
|
signed int Y_REG; //24 bit
|
||||||
|
unsigned int ADDR;
|
||||||
|
unsigned int ADRS_REG; //13 bit
|
||||||
|
|
||||||
|
void (*DoSteps)();
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
void SCSPDSP_Init(_SCSPDSP *DSP);
|
||||||
|
void SCSPDSP_SetSample(_SCSPDSP *DSP,signed int sample,int SEL,int MXL);
|
||||||
|
void SCSPDSP_Step(_SCSPDSP *DSP);
|
||||||
|
void SCSPDSP_Start(_SCSPDSP *DSP);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//#endif
|
153
Sound/SCSPLFO.cpp
Normal file
153
Sound/SCSPLFO.cpp
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
// included from SCSP.c, do not compile
|
||||||
|
|
||||||
|
//LFOs
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define LFO_SHIFT 8
|
||||||
|
|
||||||
|
struct _LFO
|
||||||
|
{
|
||||||
|
unsigned short phase;
|
||||||
|
DWORD phase_step;
|
||||||
|
int *table;
|
||||||
|
int *scale;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define LFIX(v) ((unsigned int) ((float) (1<<LFO_SHIFT)*(v)))
|
||||||
|
|
||||||
|
//Convert DB to multiply amplitude
|
||||||
|
#define DB(v) LFIX(pow(10.0,(float) (v)/20.0))
|
||||||
|
|
||||||
|
//Convert cents to step increment
|
||||||
|
#define CENTS(v) LFIX(pow(2.0,(float) (v)/1200.0))
|
||||||
|
|
||||||
|
static int PLFO_TRI[256],PLFO_SQR[256],PLFO_SAW[256],PLFO_NOI[256];
|
||||||
|
static int ALFO_TRI[256],ALFO_SQR[256],ALFO_SAW[256],ALFO_NOI[256];
|
||||||
|
static float LFOFreq[32]={0.17f,0.19f,0.23f,0.27f,0.34f,0.39f,0.45f,0.55f,0.68f,0.78f,0.92f,1.10f,1.39f,1.60f,1.87f,2.27f,
|
||||||
|
2.87f,3.31f,3.92f,4.79f,6.15f,7.18f,8.60f,10.8f,14.4f,17.2f,21.5f,28.7f,43.1f,57.4f,86.1f,172.3f};
|
||||||
|
static float ASCALE[8]={0.0f,0.4f,0.8f,1.5f,3.0f,6.0f,12.0f,24.0f};
|
||||||
|
static float PSCALE[8]={0.0f,7.0f,13.5f,27.0f,55.0f,112.0f,230.0f,494.0f};
|
||||||
|
static int PSCALES[8][256];
|
||||||
|
static int ASCALES[8][256];
|
||||||
|
|
||||||
|
void LFO_Init()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for(i=0;i<256;++i)
|
||||||
|
{
|
||||||
|
int a,p;
|
||||||
|
float TL;
|
||||||
|
//Saw
|
||||||
|
a=255-i;
|
||||||
|
if(i<128)
|
||||||
|
p=i;
|
||||||
|
else
|
||||||
|
p=255-i;
|
||||||
|
ALFO_SAW[i]=a;
|
||||||
|
PLFO_SAW[i]=p;
|
||||||
|
|
||||||
|
//Square
|
||||||
|
if(i<128)
|
||||||
|
{
|
||||||
|
a=255;
|
||||||
|
p=127;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
a=0;
|
||||||
|
p=-128;
|
||||||
|
}
|
||||||
|
ALFO_SQR[i]=a;
|
||||||
|
PLFO_SQR[i]=p;
|
||||||
|
|
||||||
|
//Tri
|
||||||
|
if(i<128)
|
||||||
|
a=255-(i*2);
|
||||||
|
else
|
||||||
|
a=(i*2)-256;
|
||||||
|
if(i<64)
|
||||||
|
p=i*2;
|
||||||
|
else if(i<128)
|
||||||
|
p=255-i*2;
|
||||||
|
else if(i<192)
|
||||||
|
p=256-i*2;
|
||||||
|
else
|
||||||
|
p=i*2-511;
|
||||||
|
ALFO_TRI[i]=a;
|
||||||
|
PLFO_TRI[i]=p;
|
||||||
|
|
||||||
|
//noise
|
||||||
|
//a=lfo_noise[i];
|
||||||
|
a=rand()&0xff;
|
||||||
|
p=128-a;
|
||||||
|
ALFO_NOI[i]=a;
|
||||||
|
PLFO_NOI[i]=p;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int s=0;s<8;++s)
|
||||||
|
{
|
||||||
|
float limit=PSCALE[s];
|
||||||
|
for(i=-128;i<128;++i)
|
||||||
|
{
|
||||||
|
PSCALES[s][i+128]=CENTS(((limit*((float) i))/128.0));
|
||||||
|
}
|
||||||
|
limit=-ASCALE[s];
|
||||||
|
for(i=0;i<256;++i)
|
||||||
|
{
|
||||||
|
ASCALES[s][i]=DB(((limit*(float) i)/256.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
signed int inline PLFO_Step(_LFO *LFO)
|
||||||
|
{
|
||||||
|
int p;
|
||||||
|
LFO->phase+=LFO->phase_step;
|
||||||
|
#if LFO_SHIFT!=8
|
||||||
|
LFO->phase&=(1<<(LFO_SHIFT+8))-1;
|
||||||
|
#endif
|
||||||
|
p=LFO->table[LFO->phase>>LFO_SHIFT];
|
||||||
|
p=LFO->scale[p+128];
|
||||||
|
return p<<(SHIFT-LFO_SHIFT);
|
||||||
|
}
|
||||||
|
|
||||||
|
signed int inline ALFO_Step(_LFO *LFO)
|
||||||
|
{
|
||||||
|
int p;
|
||||||
|
LFO->phase+=LFO->phase_step;
|
||||||
|
#if LFO_SHIFT!=8
|
||||||
|
LFO->phase&=(1<<(LFO_SHIFT+8))-1;
|
||||||
|
#endif
|
||||||
|
p=LFO->table[LFO->phase>>LFO_SHIFT];
|
||||||
|
p=LFO->scale[p];
|
||||||
|
return p<<(SHIFT-LFO_SHIFT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LFO_ComputeStep(_LFO *LFO,DWORD LFOF,DWORD LFOWS,DWORD LFOS,int ALFO)
|
||||||
|
{
|
||||||
|
float step=(float) LFOFreq[LFOF]*256.0f/(float) srate;
|
||||||
|
LFO->phase_step=(unsigned int) ((float) (1<<LFO_SHIFT)*step);
|
||||||
|
if(ALFO)
|
||||||
|
{
|
||||||
|
switch(LFOWS)
|
||||||
|
{
|
||||||
|
case 0: LFO->table=ALFO_SAW; break;
|
||||||
|
case 1: LFO->table=ALFO_SQR; break;
|
||||||
|
case 2: LFO->table=ALFO_TRI; break;
|
||||||
|
case 3: LFO->table=ALFO_NOI; break;
|
||||||
|
}
|
||||||
|
LFO->scale=ASCALES[LFOS];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch(LFOWS)
|
||||||
|
{
|
||||||
|
case 0: LFO->table=PLFO_SAW; break;
|
||||||
|
case 1: LFO->table=PLFO_SQR; break;
|
||||||
|
case 2: LFO->table=PLFO_TRI; break;
|
||||||
|
case 3: LFO->table=PLFO_NOI; break;
|
||||||
|
}
|
||||||
|
LFO->scale=PSCALES[LFOS];
|
||||||
|
}
|
||||||
|
}
|
188
Supermodel.h
Normal file
188
Supermodel.h
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
/**
|
||||||
|
** Supermodel
|
||||||
|
** A Sega Model 3 Arcade Emulator.
|
||||||
|
** Copyright 2011 Bart Trzynadlowski
|
||||||
|
**
|
||||||
|
** This file is part of Supermodel.
|
||||||
|
**
|
||||||
|
** Supermodel is free software: you can redistribute it and/or modify it under
|
||||||
|
** the terms of the GNU General Public License as published by the Free
|
||||||
|
** Software Foundation, either version 3 of the License, or (at your option)
|
||||||
|
** any later version.
|
||||||
|
**
|
||||||
|
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
** more details.
|
||||||
|
**
|
||||||
|
** You should have received a copy of the GNU General Public License along
|
||||||
|
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Supermodel.h
|
||||||
|
*
|
||||||
|
* Program-wide header file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDED_SUPERMODEL_H
|
||||||
|
#define INCLUDED_SUPERMODEL_H
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Program-Wide Definitions
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#define SUPERMODEL_VERSION "0.2-WIP" // version string
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
OS-Dependent (OSD) Items
|
||||||
|
|
||||||
|
Everything here must be provided by the OSD layer. Include files should be
|
||||||
|
located in the OSD directories for each port.
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
// stricmp() is non-standard, apparently...
|
||||||
|
#ifdef _MSC_VER // MS VisualC++
|
||||||
|
#define stricmp _stricmp
|
||||||
|
#else // assume GCC
|
||||||
|
#define stricmp strcasecmp
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fundamental Data Types:
|
||||||
|
*
|
||||||
|
* BOOL Boolean (w/ TRUE = FAIL = 1, OKAY = FALSE = 0).
|
||||||
|
* UINT64 Unsigned 64-bit integer.
|
||||||
|
* INT64 Signed 64-bit integer.
|
||||||
|
* UINT32 Unsigned 32-bit integer.
|
||||||
|
* INT32 Signed 32-bit integer.
|
||||||
|
* UINT16 Unsigned 16-bit integer.
|
||||||
|
* INT16 Signed 16-bit integer.
|
||||||
|
* UINT8 Unsigned 8-bit integer.
|
||||||
|
* INT8 Signed 8-bit integer.
|
||||||
|
* FLOAT32 Single-precision, 32-bit floating point number.
|
||||||
|
* FLOAT64 Double-precision, 64-bit floating point number.
|
||||||
|
*/
|
||||||
|
#include "Types.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Error and Debug Logging
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DebugLog(fmt, ...):
|
||||||
|
*
|
||||||
|
* Prints debugging information. The OSD layer may choose to print this to a
|
||||||
|
* log file, the screen, neither, or both. Newlines and other formatting codes
|
||||||
|
* must be explicitly included.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* fmt A format string (the same as printf()).
|
||||||
|
* ... Variable number of arguments, as required by format string.
|
||||||
|
*/
|
||||||
|
extern void DebugLog(const char *fmt, ...);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ErrorLog(fmt, ...):
|
||||||
|
*
|
||||||
|
* Prints error information. Errors need not require program termination and
|
||||||
|
* may simply be informative warnings to the user. Newlines should not be
|
||||||
|
* included in the format string -- they are automatically added at the end of
|
||||||
|
* a line.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* fmt A format string (the same as printf()).
|
||||||
|
* ... Variable number of arguments, as required by format string.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Must always return FAIL.
|
||||||
|
*/
|
||||||
|
extern BOOL ErrorLog(const char *fmt, ...);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* InfoLog(fmt, ...);
|
||||||
|
*
|
||||||
|
* Prints information to the error log file but does not print to stderr. This
|
||||||
|
* is useful for logging non-error information. Newlines are automatically
|
||||||
|
* appended.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* fmt Format string (same as printf()).
|
||||||
|
* ... Variable number of arguments as required by format string.
|
||||||
|
*/
|
||||||
|
extern void InfoLog(const char *fmt, ...);
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Header Files
|
||||||
|
|
||||||
|
All primary header files for modules used throughout Supermodel are included
|
||||||
|
here, except for external packages and APIs.
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "Games.h"
|
||||||
|
#include "ROMLoad.h"
|
||||||
|
#include "INIFile.h"
|
||||||
|
#include "BlockFile.h"
|
||||||
|
#include "Graphics/Render2D.h"
|
||||||
|
#include "Graphics/Render3D.h"
|
||||||
|
#include "Graphics/Shader.h"
|
||||||
|
#include "CPU/PowerPC/ppc.h"
|
||||||
|
#include "CPU/PowerPC/PPCDisasm.h"
|
||||||
|
#ifdef SUPERMODEL_SOUND
|
||||||
|
#include "CPU/68K/Turbo68K.h"
|
||||||
|
#endif
|
||||||
|
#include "Model3/Bus.h"
|
||||||
|
#include "Model3/IRQ.h"
|
||||||
|
#include "Model3/PCI.h"
|
||||||
|
#include "Model3/53C810.h"
|
||||||
|
#include "Model3/MPC10x.h"
|
||||||
|
#include "Model3/RTC72421.h"
|
||||||
|
#include "Model3/93C46.h"
|
||||||
|
#include "Model3/TileGen.h"
|
||||||
|
#include "Model3/Real3D.h"
|
||||||
|
#include "Sound/SCSP.h"
|
||||||
|
#include "Model3/SoundBoard.h"
|
||||||
|
#include "Model3/Model3.h"
|
||||||
|
#include "Inputs/Input.h"
|
||||||
|
#include "Inputs/Inputs.h"
|
||||||
|
#include "Inputs/InputSource.h"
|
||||||
|
#include "Inputs/InputSystem.h"
|
||||||
|
#include "Inputs/InputTypes.h"
|
||||||
|
#include "Inputs/MultiInputSource.h"
|
||||||
|
|
||||||
|
/******************************************************************************
|
||||||
|
Helpful Macros and Inlines
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FLIPENDIAN16(data):
|
||||||
|
* FLIPENDIAN32(data):
|
||||||
|
*
|
||||||
|
* Flips the endianness of the data (reverses bytes).
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* data Word or half-word to flip.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Flipped word.
|
||||||
|
*/
|
||||||
|
static inline UINT16 FLIPENDIAN16(UINT16 d)
|
||||||
|
{
|
||||||
|
return(((d >> 8) & 0x00FF) |
|
||||||
|
((d << 8) & 0xFF00));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline UINT32 FLIPENDIAN32(UINT32 d)
|
||||||
|
{
|
||||||
|
return(((d >> 24) & 0x000000FF) |
|
||||||
|
((d >> 8) & 0x0000FF00) |
|
||||||
|
((d << 8) & 0x00FF0000) |
|
||||||
|
((d << 24) & 0xFF000000));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // INCLUDED_SUPERMODEL_H
|
Loading…
Reference in a new issue