697 lines
14 KiB
C++
697 lines
14 KiB
C++
/*
|
|
(The MIT License)
|
|
|
|
Copyright (c) 2008-2016 by
|
|
David Etherton, Eric Anderton, Alec Bourque (Uze), Filipe Rinaldi,
|
|
Sandor Zsuga (Jubatian), Matt Pandina (Artcfox)
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
THE SOFTWARE.
|
|
*/
|
|
#pragma once
|
|
|
|
#include <vector>
|
|
#include <stdint.h>
|
|
#include <queue>
|
|
#include <cstring>
|
|
|
|
// Video: Offset of display on the emulator's surface
|
|
// Syncronized with the kernel, this value now results in the image
|
|
// being perfectly centered in both the emulator and a real TV
|
|
#define VIDEO_LEFT_EDGE 168U
|
|
// Video: Display width; the width of the emulator's output (before any
|
|
// scaling applied) and video capturing
|
|
#define VIDEO_DISP_WIDTH 720U
|
|
|
|
//Uzebox keyboard defines
|
|
#define KB_STOP 0
|
|
#define KB_TX_START 1
|
|
#define KB_TX_READY 2
|
|
|
|
#define KB_SEND_KEY 0x00
|
|
#define KB_SEND_END 0x01
|
|
#define KB_SEND_DEVICE_ID 0x02
|
|
#define KB_SEND_FIRMWARE_REV 0x03
|
|
#define KB_RESET 0x7f
|
|
|
|
// Joysticks
|
|
#define MAX_JOYSTICKS 2
|
|
#define NUM_JOYSTICK_BUTTONS 8
|
|
#define MAX_JOYSTICK_AXES 8
|
|
#define MAX_JOYSTICK_HATS 8
|
|
|
|
#define JOY_SNES_X 0
|
|
#define JOY_SNES_A 1
|
|
#define JOY_SNES_B 2
|
|
#define JOY_SNES_Y 3
|
|
#define JOY_SNES_LSH 6
|
|
#define JOY_SNES_RSH 7
|
|
#define JOY_SNES_SELECT 8
|
|
#define JOY_SNES_START 9
|
|
|
|
#define JOY_DIR_UP 1
|
|
#define JOY_DIR_RIGHT 2
|
|
#define JOY_DIR_DOWN 4
|
|
#define JOY_DIR_LEFT 8
|
|
#define JOY_DIR_COUNT 4
|
|
#define JOY_AXIS_UNUSED -1
|
|
|
|
#define JOY_MASK_UP 0x11111111
|
|
#define JOY_MASK_RIGHT 0x22222222
|
|
#define JOY_MASK_DOWN 0x44444444
|
|
#define JOY_MASK_LEFT 0x88888888
|
|
|
|
#ifndef JOY_ANALOG_DEADZONE
|
|
#define JOY_ANALOG_DEADZONE 4096
|
|
#endif
|
|
|
|
#if defined(_MSC_VER) && _MSC_VER >= 1400
|
|
// don't whine about sprintf and fopen.
|
|
// could switch to sprintf_s but that's not standard.
|
|
#pragma warning(disable : 4996)
|
|
#endif
|
|
|
|
// 644 Overview: http://www.atmel.com/dyn/resources/prod_documents/doc2593.pdf
|
|
// AVR8 insn set: http://www.atmel.com/dyn/resources/prod_documents/doc0856.pdf
|
|
|
|
enum
|
|
{
|
|
NES_A,
|
|
NES_B,
|
|
PAD_SELECT,
|
|
PAD_START,
|
|
PAD_UP,
|
|
PAD_DOWN,
|
|
PAD_LEFT,
|
|
PAD_RIGHT
|
|
};
|
|
enum
|
|
{
|
|
SNES_B,
|
|
SNES_Y,
|
|
SNES_A = 8,
|
|
SNES_X,
|
|
SNES_LSH,
|
|
SNES_RSH
|
|
};
|
|
|
|
#if 1 // 644P
|
|
const unsigned eepromSize = 2048;
|
|
const unsigned sramSize = 4096;
|
|
const unsigned progSize = 65536;
|
|
#else // 1284P
|
|
const unsigned eepromSize = 4096;
|
|
const unsigned sramSize = 16384;
|
|
const unsigned progSize = 131072;
|
|
#endif
|
|
|
|
#define IOBASE 32
|
|
#define SRAMBASE 256
|
|
|
|
namespace ports
|
|
{
|
|
enum
|
|
{
|
|
PINA,
|
|
DDRA,
|
|
PORTA,
|
|
PINB,
|
|
DDRB,
|
|
PORTB,
|
|
PINC,
|
|
DDRC,
|
|
PORTC,
|
|
PIND,
|
|
DDRD,
|
|
PORTD,
|
|
res2C,
|
|
res2D,
|
|
res2E,
|
|
res2F,
|
|
res30,
|
|
res31,
|
|
res32,
|
|
res33,
|
|
res34,
|
|
TIFR0,
|
|
TIFR1,
|
|
TIFR2,
|
|
res38,
|
|
res39,
|
|
res3A,
|
|
PCIFR,
|
|
EIFR,
|
|
EIMSK,
|
|
GPIOR0,
|
|
EECR,
|
|
EEDR,
|
|
EEARL,
|
|
EEARH,
|
|
GTCCR,
|
|
TCCR0A,
|
|
TCCR0B,
|
|
TCNT0,
|
|
OCR0A,
|
|
OCR0B,
|
|
res49,
|
|
GPIOR1,
|
|
GPIOR2,
|
|
SPCR,
|
|
SPSR,
|
|
SPDR,
|
|
res4f,
|
|
ACSR,
|
|
OCDR,
|
|
res52,
|
|
SMCR,
|
|
MCUSR,
|
|
MCUCR,
|
|
res56,
|
|
SPMCSR,
|
|
res58,
|
|
res59,
|
|
res5A,
|
|
res5B,
|
|
res5C,
|
|
SPL,
|
|
SPH,
|
|
SREG,
|
|
WDTCSR,
|
|
CLKPR,
|
|
res62,
|
|
res63,
|
|
PRR,
|
|
res65,
|
|
OSCCAL,
|
|
res67,
|
|
PCICR,
|
|
EICRA,
|
|
res6a,
|
|
PCMSK0,
|
|
PCMSK1,
|
|
PCMSK2,
|
|
TIMSK0,
|
|
TIMSK1,
|
|
TIMSK2,
|
|
res71,
|
|
res72,
|
|
PCMSK3,
|
|
res74,
|
|
res75,
|
|
res76,
|
|
res77,
|
|
ADCL,
|
|
ADCH,
|
|
ADCSRA,
|
|
ADCSRB,
|
|
ADMUX,
|
|
res7d,
|
|
DIDR0,
|
|
DIDR1,
|
|
TCCR1A,
|
|
TCCR1B,
|
|
TCCR1C,
|
|
res83,
|
|
TCNT1L,
|
|
TCNT1H,
|
|
ICR1L,
|
|
ICR1H,
|
|
OCR1AL,
|
|
OCR1AH,
|
|
OCR1BL,
|
|
OCR1BH,
|
|
res8c,
|
|
res8d,
|
|
res8e,
|
|
res8f,
|
|
res90,
|
|
res91,
|
|
res92,
|
|
res93,
|
|
res94,
|
|
res95,
|
|
res96,
|
|
res97,
|
|
res98,
|
|
res99,
|
|
res9a,
|
|
res9b,
|
|
res9c,
|
|
res9d,
|
|
res9e,
|
|
res9f,
|
|
resa0,
|
|
resa1,
|
|
resa2,
|
|
resa3,
|
|
resa4,
|
|
resa5,
|
|
resa6,
|
|
resa7,
|
|
resa8,
|
|
resa9,
|
|
resaa,
|
|
resab,
|
|
resac,
|
|
resad,
|
|
resae,
|
|
resaf,
|
|
TCCR2A,
|
|
TCCR2B,
|
|
TCNT2,
|
|
OCR2A,
|
|
OCR2B,
|
|
resb5,
|
|
ASSR,
|
|
resb7,
|
|
TWBR,
|
|
TWSR,
|
|
TWAR,
|
|
TWDR,
|
|
TWCR,
|
|
TWAMR,
|
|
resbe,
|
|
resbf,
|
|
UCSR0A,
|
|
UCSR0B,
|
|
UCSR0C,
|
|
resc3,
|
|
UBRR0L,
|
|
UBRR0H,
|
|
UDR0,
|
|
resc7,
|
|
resc8,
|
|
resc9,
|
|
resca,
|
|
rescb,
|
|
rescc,
|
|
rescd,
|
|
resce,
|
|
rescf,
|
|
resd0,
|
|
resd1,
|
|
resd2,
|
|
resd3,
|
|
resd4,
|
|
resd5,
|
|
resd6,
|
|
resd7,
|
|
resd8,
|
|
resd9,
|
|
resda,
|
|
resdb,
|
|
resdc,
|
|
resdd,
|
|
resde,
|
|
resdf,
|
|
rese0,
|
|
rese1,
|
|
rese2,
|
|
rese3,
|
|
rese4,
|
|
rese5,
|
|
rese6,
|
|
rese7,
|
|
rese8,
|
|
rese9,
|
|
resea,
|
|
reseb,
|
|
resec,
|
|
resed,
|
|
resee,
|
|
resef,
|
|
resf0,
|
|
resf1,
|
|
resf2,
|
|
resf3,
|
|
resf4,
|
|
resf5,
|
|
resf6,
|
|
resf7,
|
|
resf8,
|
|
resf9,
|
|
resfa,
|
|
resfb,
|
|
resfc,
|
|
resfd,
|
|
resfe,
|
|
resff
|
|
};
|
|
}
|
|
|
|
typedef uint8_t u8;
|
|
typedef int8_t s8;
|
|
typedef uint16_t u16;
|
|
typedef int16_t s16;
|
|
typedef uint32_t u32;
|
|
typedef int32_t s32;
|
|
|
|
typedef struct
|
|
{
|
|
s16 arg2;
|
|
u8 arg1;
|
|
u8 opNum;
|
|
} __attribute__((packed)) instructionDecode_t;
|
|
|
|
typedef struct
|
|
{
|
|
u8 opNum;
|
|
char opName[32];
|
|
u8 arg1Type;
|
|
u8 arg1Mul;
|
|
u8 arg1Offset;
|
|
u8 arg1Neg;
|
|
u8 arg2Type;
|
|
u8 arg2Mul;
|
|
u8 arg2Offset;
|
|
u8 arg2Neg;
|
|
u8 words;
|
|
u8 clocks;
|
|
u16 mask;
|
|
u16 arg1Mask;
|
|
u16 arg2Mask;
|
|
} instructionList_t;
|
|
|
|
using namespace std;
|
|
|
|
//SPI state machine states
|
|
enum
|
|
{
|
|
SPI_IDLE_STATE,
|
|
SPI_ARG_X_LO,
|
|
SPI_ARG_X_HI,
|
|
SPI_ARG_Y_LO,
|
|
SPI_ARG_Y_HI,
|
|
SPI_ARG_CRC,
|
|
SPI_RESPOND_SINGLE,
|
|
SPI_RESPOND_MULTI,
|
|
SPI_READ_SINGLE_BLOCK,
|
|
SPI_READ_MULTIPLE_BLOCK,
|
|
SPI_WRITE_SINGLE,
|
|
SPI_WRITE_SINGLE_BLOCK,
|
|
SPI_RESPOND_R1,
|
|
SPI_RESPOND_R1B,
|
|
SPI_RESPOND_R2,
|
|
SPI_RESPOND_R3,
|
|
SPI_RESPOND_R7,
|
|
};
|
|
|
|
struct SDPartitionEntry
|
|
{
|
|
u8 state;
|
|
u8 startHead;
|
|
u16 startCylinder;
|
|
u8 type;
|
|
u8 endHead;
|
|
u16 endCylinder;
|
|
u32 sectorOffset;
|
|
u32 sectorCount;
|
|
};
|
|
|
|
struct avr8
|
|
{
|
|
avr8() : /*Core*/
|
|
pc(0),
|
|
watchdogTimer(0), prevPortB(0), prevWDR(0),
|
|
dly_out(0), itd_TIFR1(0), elapsedCyclesSleep(0),
|
|
timer1_next(0), timer1_base(0), TCNT1(0),
|
|
//to align with AVR Simulator 2 since it has a bug that the first JMP
|
|
//at the reset vector takes only 2 cycles
|
|
cycleCounter(-1),
|
|
|
|
/*Video*/
|
|
video_buffer(nullptr),
|
|
|
|
/*Audio*/
|
|
enableSound(true),
|
|
|
|
/*Joystick*/
|
|
lagged(false),
|
|
|
|
/*Uzekeyboard*/
|
|
uzeKbState(0), uzeKbEnabled(false),
|
|
|
|
/*SPI Emulation*/
|
|
spiByte(0), spiClock(0), spiTransfer(0), spiState(SPI_IDLE_STATE), spiResponsePtr(0), spiResponseEnd(0)
|
|
{
|
|
memset(r, 0, sizeof(r));
|
|
memset(io, 0, sizeof(io));
|
|
memset(sram, 0, sizeof(sram));
|
|
memset(eeprom, 0, sizeof(eeprom));
|
|
memset(progmem, 0, progSize / 2);
|
|
memset(progmemDecoded, 0, progSize / 2);
|
|
}
|
|
|
|
/*Core*/
|
|
u16 progmem[progSize / 2];
|
|
instructionDecode_t progmemDecoded[progSize / 2];
|
|
u16 pc, currentPc;
|
|
|
|
private:
|
|
unsigned int cycleCounter;
|
|
unsigned int elapsedCycles, prevCyclesCounter, elapsedCyclesSleep, lastCyclesSleep;
|
|
unsigned int prevPortB, prevWDR;
|
|
unsigned int watchdogTimer;
|
|
unsigned int cycle_ctr_ins; // Used in update_hardware_ins to track elapsed cycles between calls
|
|
// u8 eeClock; TODO: Only set at one location, never used. Maybe a never completed EEPROM timing code.
|
|
unsigned int T16_latch; // Latch for 16-bit timers (16 bits used)
|
|
unsigned int TCNT1; // Timer 1 counter (used instead of TCNT1H:TCNT1L)
|
|
unsigned int timer1_next; // Cycles remaining until next timer1 event
|
|
unsigned int timer1_base; // Where the between-events timer started (to reproduce TCNT1)
|
|
unsigned int itd_TIFR1; // Interrupt delaying for TIFR1 (8 bits used)
|
|
unsigned int dly_out; // Delayed output flags
|
|
unsigned int dly_TCCR1B; // Delayed Timer1 controls
|
|
unsigned int dly_TCNT1L; // Delayed Timer1 count (low)
|
|
unsigned int dly_TCNT1H; // Delayed Timer1 count (high)
|
|
public:
|
|
int randomSeed;
|
|
u16 decodeArg(u16 flash, u16 argMask, u8 argNeg);
|
|
void instructionDecode(u16 address);
|
|
void decodeFlash(void);
|
|
void decodeFlash(u16 address);
|
|
|
|
struct
|
|
{
|
|
union {
|
|
u8 r[32]; // Register file
|
|
struct
|
|
{
|
|
u8 r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15;
|
|
u8 r16, r17, r18, r19, r20, r21, r22, r23, r24, r25, XL, XH, YL, YH, ZL, ZH;
|
|
};
|
|
};
|
|
union {
|
|
u8 io[256]; // Direct-mapped I/O space
|
|
struct
|
|
{
|
|
u8 PINA, DDRA, PORTA, PINB, DDRB, PORTB, PINC, DDRC;
|
|
u8 PORTC, PIND, DDRD, PORTD, res2C, res2D, res2E, res2F;
|
|
u8 res30, res31, res32, res33, res34, TIFR0, TIFR1, TIFR2;
|
|
u8 res38, res39, res3A, PCIFR, EIFR, EIMSK, GPIOR0, EECR;
|
|
u8 EEDR, EEARL, EEARH, GTCCR, TCCR0A, TCCR0B, TCNT0, OCR0A;
|
|
u8 OCR0B, res49, GPIOR1, GPIOR2, SPCR, SPSR, SPDR, res4f;
|
|
u8 ACSR, OCDR, res52, SMCR, MCUSR, MCUCR, res56, SPMCSR;
|
|
u8 res58, res59, res5A, res5B, res5C, SPL, SPH, SREG;
|
|
u8 WDTCSR, CLKPR, res62, res63, PRR, res65, OSCCAL, res67;
|
|
u8 PCICR, EICRA, res6a, PCMSK0, PCMSK1, PCMSK2, TIMSK0, TIMSK1;
|
|
u8 TIMSK2, res71, res72, PCMSK3, res74, res75, res76, res77;
|
|
u8 ADCL, ADCH, ADCSRA, ADCSRB, ADMUX, res7d, DIDR0, DIDR1;
|
|
u8 TCCR1A, TCCR1B, TCCR1C, res83, TCNT1L, TCNT1H, ICR1L, ICR1H;
|
|
u8 OCR1AL, OCR1AH, OCR1BL, OCR1BH, res8c, res8d, res8e, res8f;
|
|
u8 res90, res91, res92, res93, res94, res95, res96, res97;
|
|
u8 res98, res99, res9a, res9b, res9c, res9d, res9e, res9f;
|
|
u8 resa0, resa1, resa2, resa3, resa4, resa5, resa6, resa7;
|
|
u8 resa8, resa9, resaa, resab, resac, resad, resae, resaf;
|
|
u8 TCCR2A, TCCR2B, TCNT2, OCR2A, OCR2B, resb5, ASSR, resb7;
|
|
u8 TWBR, TWSR, TWAR, TWDR, TWCR, TWAMR, resbe, resbf;
|
|
u8 UCSR0A, UCSR0B, UCSR0C, resc3, UBRR0L, UBRR0H, UDR0, resc7;
|
|
u8 resc8, resc9, resca, rescb, rescc, rescd, resce, rescf;
|
|
u8 resd0, resd1, resd2, resd3, resd4, resd5, resd6, resd7;
|
|
u8 resd8, resd9, resda, resdb, resdc, resdd, resde, resdf;
|
|
u8 rese0, rese1, rese2, rese3, rese4, rese5, rese6, rese7;
|
|
u8 rese8, rese9, resea, reseb, resec, resed, resee, resef;
|
|
u8 resf0, resf1, resf2, resf3, resf4, resf5, resf6, resf7;
|
|
u8 resf8, resf9, resfa, resfb, resfc, resfd, resfe, resff;
|
|
};
|
|
};
|
|
u8 sram[sramSize];
|
|
};
|
|
u8 eeprom[eepromSize];
|
|
|
|
int scanline_count;
|
|
unsigned int left_edge_cycle;
|
|
int scanline_top;
|
|
unsigned int left_edge;
|
|
u32 inset;
|
|
u32 palette[256];
|
|
u8 scanline_buf[2048]; // For collecting pixels from a single scanline
|
|
u8 pixel_raw; // Raw (8 bit) input pixel
|
|
|
|
u32 *video_buffer;
|
|
|
|
/*Audio*/
|
|
bool enableSound;
|
|
|
|
/*Joystick*/
|
|
// SNES bit order: 0 = B, Y, Select, Start, Up, Down, Left, Right, A, X, L, 11 = R
|
|
// NES bit order: 0 = A, B, Select, Start, Up, Down, Left, 7 = Right
|
|
u32 buttons[2], latched_buttons[2];
|
|
bool lagged;
|
|
|
|
/*Uzebox Keyboard*/
|
|
u8 uzeKbState;
|
|
u8 uzeKbDataOut;
|
|
bool uzeKbEnabled;
|
|
queue<u8> uzeKbScanCodeQueue;
|
|
u8 uzeKbDataIn;
|
|
u8 uzeKbClock;
|
|
|
|
/*SPI Emulation*/
|
|
u8 spiByte;
|
|
u8 spiTransfer;
|
|
u16 spiClock;
|
|
u16 spiCycleWait;
|
|
u8 spiState;
|
|
u8 spiCommand;
|
|
u8 spiCommandDelay;
|
|
union {
|
|
u32 spiArg;
|
|
union {
|
|
struct
|
|
{
|
|
u16 spiArgY;
|
|
u16 spiArgX;
|
|
};
|
|
struct
|
|
{
|
|
u8 spiArgYlo;
|
|
u8 spiArgYhi;
|
|
u8 spiArgXlo;
|
|
u8 spiArgXhi;
|
|
};
|
|
};
|
|
};
|
|
u32 spiByteCount;
|
|
u8 spiResponseBuffer[12];
|
|
u8 *spiResponsePtr;
|
|
u8 *spiResponseEnd;
|
|
|
|
private:
|
|
void write_io(u8 addr, u8 value);
|
|
u8 read_io(u8 addr);
|
|
// Should not be called directly (see write_io)
|
|
void write_io_x(u8 addr, u8 value);
|
|
|
|
inline u8 read_progmem(u16 addr)
|
|
{
|
|
u16 word = progmem[addr >> 1];
|
|
return (addr & 1) ? word >> 8 : word;
|
|
}
|
|
|
|
inline void write_sram(u16 addr, u8 value)
|
|
{
|
|
sram[(addr - SRAMBASE) & (sramSize - 1U)] = value;
|
|
}
|
|
|
|
void write_sram_io(u16 addr, u8 value)
|
|
{
|
|
if (addr >= SRAMBASE)
|
|
{
|
|
sram[(addr - SRAMBASE) & (sramSize - 1)] = value;
|
|
}
|
|
else if (addr >= IOBASE)
|
|
{
|
|
write_io(addr - IOBASE, value);
|
|
}
|
|
else
|
|
{
|
|
r[addr] = value; // Write a register
|
|
}
|
|
}
|
|
|
|
inline u8 read_sram(u16 addr)
|
|
{
|
|
return sram[(addr - SRAMBASE) & (sramSize - 1U)];
|
|
}
|
|
|
|
u8 read_sram_io(u16 addr)
|
|
{
|
|
|
|
if (addr >= SRAMBASE)
|
|
{
|
|
return sram[(addr - SRAMBASE) & (sramSize - 1)];
|
|
}
|
|
else if (addr >= IOBASE)
|
|
{
|
|
return read_io(addr - IOBASE);
|
|
}
|
|
else
|
|
{
|
|
return r[addr]; // Read a register
|
|
}
|
|
}
|
|
|
|
inline static unsigned int get_insn_size(unsigned int insn)
|
|
{
|
|
/* 41 LDS Rd,k (next word is rest of address)
|
|
82 STS k,Rr (next word is rest of address)
|
|
30 JMP k (next word is rest of address)
|
|
14 CALL k (next word is rest of address) */
|
|
// This code is simplified by assuming upper k bits are zero on 644
|
|
|
|
if (insn == 14 || insn == 30 || insn == 41 || insn == 82)
|
|
{
|
|
return 2U;
|
|
}
|
|
else
|
|
{
|
|
return 1U;
|
|
}
|
|
}
|
|
|
|
public:
|
|
bool init_gui();
|
|
void draw_memorymap();
|
|
void trigger_interrupt(unsigned int location);
|
|
unsigned int exec();
|
|
void spi_calculateClock();
|
|
void update_hardware();
|
|
void update_hardware_fast();
|
|
void update_hardware_ins();
|
|
void update_spi();
|
|
u8 SDReadByte();
|
|
void SDWriteByte(u8 value);
|
|
void SDCommit();
|
|
void LoadEEPROMFile(const char *filename);
|
|
void shutdown(int errcode);
|
|
void idle(void);
|
|
};
|
|
|
|
// undefine the following to disable SPI debug messages
|
|
#ifdef USE_SPI_DEBUG
|
|
#define SPI_DEBUG(...) printf(__VA_ARGS__)
|
|
#else
|
|
#define SPI_DEBUG(...)
|
|
#endif
|
|
|
|
#ifdef USE_EEPROM_DEBUG
|
|
#define EEPROM_DEBUG(...) printf(__VA_ARGS__)
|
|
#else
|
|
#define EEPROM_DEBUG(...)
|
|
#endif
|