Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Boilerplate #1

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions gbemu/gbemu/common/emusys.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stddef.h>
#include <assert.h>
#include <ctype.h>

#define _USE_MATH_DEFINES
#include <math.h>

typedef unsigned char byte;
typedef unsigned char uint8;
typedef signed char int8;
typedef unsigned short uint16;
typedef signed short int16;
typedef unsigned int uint32;
typedef signed int int32;
typedef unsigned int uint;
typedef signed long long int64;
typedef unsigned long long uint64;
Comment on lines +12 to +21

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kinda risky, use https://cplusplus.com/reference/cstdint/ for full portability

99 changes: 99 additions & 0 deletions gbemu/gbemu/common/endianness.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#include "emusys.h"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in general, try to use constexpr as much as possible and macros as a last resort, you can even use constexpr to determine if the platform is big endian or little endian at compiletime


// Below macros are used for converting
// Little Endian data to Big Endian and vice versa
// This uses MSVC's and GCC's stdlib header
// Using inline implmentations for other compilers
#if defined(__GNUC__)
#define SWAP_BYTES_64(a) __builtin_bswap64(a)
#define SWAP_BYTES_32(a) __builtin_bswap32(a)
#define SWAP_BYTES_16(a) __builtin_bswap16(a)
#elif defined(_MSC_VER)
#define SWAP_BYTES_64(a) _byteswap_uint64(a)
#define SWAP_BYTES_32(a) _byteswap_ulong(a)
#define SWAP_BYTES_16(a) _byteswap_ushort(a)
#else
inline uint64 SWAP_BYTES_64(uint64 a) {
uint32 low = (uint32)a, high = (uint32)(a >> 32);
uint16 lowLow = (uint16)low, lowHigh = (uint16)(low >> 16), highLow = (uint16)high, highHigh = (uint16)(high >> 16);

return ((uint64)(((uint32)(uint16)((lowLow >> 8) | (lowLow << 8)) << 16) | (uint16)((lowHigh >> 8) | (lowHigh << 8))) << 32) | (((uint32)(uint16)((highLow >> 8) | (highLow << 8)) << 16) | (uint16)((highHigh >> 8) | (highHigh << 8)));
}
inline uint32 SWAP_BYTES_32(uint32 a) {
const uint16 low = (uint16)a, high = (uint16)(a >> 16);
return ((uint32)(uint16)((low >> 8) | (low << 8)) << 16) | (uint16)((high >> 8) | (high << 8));
}
inline uint16 SWAP_BYTES_16(const uint16 a) {
return (a >> 8) | (a << 8);
}
#endif


// We assume the machine to be Little Endian by default
// As this is running on Intel, AMD and Apple Silicon chips
// Will add compatibility for Big Endian machine later

inline uint16 READ_UINT16(const void *ptr) {
const uint8 *b = (const uint8 *)ptr;
return (b[1] << 8) | b[0];
}
Comment on lines +36 to +39

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you're writing for little endian, why not do:

inline uint16 READ_UINT16(const void *ptr) {
	const uint16 *b = (const uint16 *)ptr;
	return *b;
}

you can even do this

template<typename T>
static inline T READ(const void *ptr) {
	const uint16 *b = (const uint16 *)ptr;
	return *b;
}

// And if you don't want to use READ<uint32> everywhere:
static inline uint32 READ_UINT32(const void *ptr) {
       return READ<uint32>(ptr);
}
...

(try using static inline as much as you can)

Also please avoid using void* in cpp, typedef or string typedef to signify that this is a stream.

Using constexprs you can even write a common definition for loading LE values on a BE and LE system:

template<typename T>
constexpr static inline T READ(const void *ptr) {
        // set isBigEnding somewhere using constexpers to detect
        if constexpr (isBigEndian) {
                constexpr size_t byteCount = sizeof(T);
		T res = 0;
		// This will be unrolled easily
		for(size_t i = byteCount - 1; i >= 0; i-- ) {
		        res |= (((T*)ptr)[i] << (i * 8));
                }
                return res;
        }
        else {
	        const uint16 *b = (const uint16 *)ptr;
		return *b;
        }
}

^ This can very easily be extended to get support BE values on a BE and LE system as well with a couple of more if branches and is pretty idiomatic.


inline uint32 READ_UINT32(const void *ptr) {
const uint8 *b = (const uint8 *)ptr;
return (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | (b[0]);
}

inline uint64 READ_UINT64(const void *ptr) {
const uint8 *b = (const uint8 *)ptr;
return ((uint64)b[7] << 56) | ((uint64)b[6] << 48) | ((uint64)b[5] << 40) | ((uint64)b[4] << 32) | ((uint64)b[3] << 24) | ((uint64)b[2] << 16) | ((uint64)b[1] << 8) | ((uint64)b[0]);
}

inline void WRITE_UINT16(void *ptr, uint16 value) {
uint8 *b = (uint8 *)ptr;
b[0] = (uint8)(value >> 0);
b[1] = (uint8)(value >> 8);
}

inline void WRITE_UINT32(void *ptr, uint32 value) {
uint8 *b = (uint8 *)ptr;
b[0] = (uint8)(value >> 0);
b[1] = (uint8)(value >> 8);
b[2] = (uint8)(value >> 16);
b[3] = (uint8)(value >> 24);
}

inline void WRITE_UINT64(void *ptr, uint64 value) {
uint8 *b = (uint8 *)ptr;
b[0] = (uint8)(value >> 0);
b[1] = (uint8)(value >> 8);
b[2] = (uint8)(value >> 16);
b[3] = (uint8)(value >> 24);
b[4] = (uint8)(value >> 32);
b[5] = (uint8)(value >> 40);
b[6] = (uint8)(value >> 48);
b[7] = (uint8)(value >> 56);
}

// Used in Common::Stream
#define READ_LE_UINT16(a) READ_UINT16(a)
#define READ_LE_UINT32(a) READ_UINT32(a)
#define READ_LE_UINT64(a) READ_UINT64(a)
#define WRITE_LE_UINT16(a, v) WRITE_UINT16(a, v)
#define WRITE_LE_UINT32(a, v) WRITE_UINT32(a, v)
#define WRITE_LE_UINT64(a, v) WRITE_UINT64(a, v)
#define FROM_LE_16(a) ((uint16)(a))
#define FROM_LE_32(a) ((uint32)(a))
#define FROM_LE_64(a) ((uint64)(a))
#define FROM_BE_16(a) SWAP_BYTES_16(a)
#define FROM_BE_32(a) SWAP_BYTES_32(a)
#define FROM_BE_64(a) SWAP_BYTES_64(a)
#define TO_LE_16(a) ((uint16)(a))
#define TO_LE_32(a) ((uint32)(a))
#define TO_LE_64(a) ((uint64)(a))
#define TO_BE_16(a) SWAP_BYTES_16(a)
#define TO_BE_32(a) SWAP_BYTES_32(a)
#define TO_BE_64(a) SWAP_BYTES_64(a)

// Might come handy later
#define LITTLE_ENDIAN false
#define BIG_ENDIAN true
88 changes: 88 additions & 0 deletions gbemu/gbemu/common/stream.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//Taken from ScummVM's Common::Stream

#include "endianness.h"

namespace Common {
class Stream {
public:
virtual ~Stream() {}

// Below functions throw error and reset the error flag
// Similar to ferror() and clearerr()

virtual bool err() const { return false; }
virtual void clearError() {}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make this a purely virtual function

};

class ReadStream : virtual public Stream {
private:
bool isBigEndian;

public:
ReadStream(bool bigEndian = LITTLE_ENDIAN) : isBigEndian(bigEndian) {}

bool isBE() { return isBigEndian; }

// End of stream interface
virtual bool eos() const = 0;

// Read interface
virtual uint32 read(void *dataPtr, uint32 dataSize) = 0;

byte readByte() {
byte b = 0;
read(&b, 1);
return b;
}

uint16 readUint16() {
uint16 val;
read(&val, 2);
return (isBigEndian) ? FROM_BE_16(val) : FROM_LE_16(val);
}

uint32 readUint32() {
uint32 val;
read(&val, 4);
return (isBigEndian) ? FROM_BE_32(val) : FROM_LE_32(val);
}

uint64 readUint64() {
uint64 val;
read(&val, 8);
return (isBigEndian) ? FROM_BE_64(val) : FROM_LE_64(val);
}

inline byte readSByte() {
return (int8) readByte();
}

inline int16 readSint16() {
return (int16) readUint16();
}

inline int32 readSint32() {
return (int32) readUint32();
}

inline int64 readSint64() {
return (int64) readUint64();
}
};

class SeekableReadStream : virtual public ReadStream {
public:
// Position of cursor
virtual int64 pos() const = 0;

// Size of stream
virtual int64 size() const = 0;

// Set the cursor to a specific place in stream
// wrapper identical to fseek()
virtual bool seek(int64 offset, int whence = SEEK_SET) = 0;

// Skip given bytes in stream
virtual bool skip(uint32 offset) { return seek(offset, SEEK_CUR); }
};
}
20 changes: 20 additions & 0 deletions gbemu/gbemu/gbemu.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#include "common/stream.h"

int main()
{
char* k = new char[10];
printf_s("Hello World\n");
scanf_s(k);
return 0;
}

// Run program: Ctrl + F5 or Debug > Start Without Debugging menu
// Debug program: F5 or Debug > Start Debugging menu

// Tips for Getting Started:
// 1. Use the Solution Explorer window to add/manage files
// 2. Use the Team Explorer window to connect to source control
// 3. Use the Output window to see build output and other messages
// 4. Use the Error List window to view errors
// 5. Go to Project > Add New Item to create new code files, or Project > Add Existing Item to add existing code files to the project
// 6. In the future, to open this project again, go to File > Open > Project and select the .sln file
Comment on lines +11 to +20

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove kek