├── XTulator ├── modules │ ├── input │ │ ├── input.h │ │ ├── mouse.h │ │ ├── sdlkeys.h │ │ └── mouse.c │ ├── audio │ │ ├── sdlaudio.h │ │ ├── pcspeaker.h │ │ ├── blaster.h │ │ ├── opl2.h │ │ ├── pcspeaker.c │ │ ├── nukedopl.h │ │ ├── sdlaudio.c │ │ ├── opl2.c │ │ └── blaster.c │ ├── io │ │ ├── pcap-win32.h │ │ ├── tcpmodem.h │ │ ├── pcap-win32.c │ │ ├── bswap.h │ │ └── ne2000.h │ ├── video │ │ ├── sdlconsole.h │ │ ├── cga.h │ │ ├── vga.h │ │ ├── sdlconsole.c │ │ └── cga.c │ └── disk │ │ ├── biosdisk.h │ │ ├── fdc.h │ │ └── biosdisk.c ├── args.h ├── utility.h ├── rtc.h ├── memory.h ├── debuglog.h ├── chipset │ ├── i8255.h │ ├── i8237.h │ ├── uart.h │ ├── i8259.h │ ├── i8253.h │ ├── i8255.c │ ├── i8259.c │ ├── uart.c │ ├── i8253.c │ └── i8237.c ├── ports.h ├── timing.h ├── menus.h ├── config.h ├── cpu │ ├── cpuconf.h │ └── cpu.h ├── debuglog.c ├── utility.c ├── machine.h ├── memory.c ├── rtc.c ├── ports.c ├── main.c ├── timing.c ├── menus.c ├── XTulator.vcxproj.filters └── machine.c ├── .gitignore ├── TODO.txt ├── XTulator.sln └── README.md /XTulator/modules/input/input.h: -------------------------------------------------------------------------------- 1 | #ifndef _INPUT_H_ 2 | #define _INPUT_H_ 3 | 4 | #include 5 | 6 | typedef struct { 7 | uint8_t scancode; 8 | uint8_t isNew; 9 | } KEYSTATE_t; 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /XTulator/args.h: -------------------------------------------------------------------------------- 1 | #ifndef _ARGS_H_ 2 | #define _ARGS_H_ 3 | 4 | #include 5 | #include "machine.h" 6 | 7 | int args_parse(MACHINE_t* machine, int argc, char* argv[]); 8 | void args_showHelp(); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /XTulator/utility.h: -------------------------------------------------------------------------------- 1 | #ifndef _UTILITY_H_ 2 | #define _UTILITY_H_ 3 | 4 | #include 5 | 6 | int utility_loadFile(uint8_t* dst, size_t len, char* srcfile); 7 | void utility_sleep(uint32_t ms); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /XTulator/rtc.h: -------------------------------------------------------------------------------- 1 | #ifndef _RTC_H_ 2 | #define _RTC_H_ 3 | 4 | #include 5 | #include "cpu/cpu.h" 6 | 7 | uint8_t rtc_read(void* dummy, uint16_t addr); 8 | void rtc_write(void* dummy, uint16_t addr, uint8_t value); 9 | void rtc_init(); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #Ignore thumbnails created by Windows 2 | Thumbs.db 3 | #Ignore files built by Visual Studio 4 | *.user 5 | *.aps 6 | *.pch 7 | *.vspscc 8 | *_i.c 9 | *_p.c 10 | *.ncb 11 | *.suo 12 | *.bak 13 | *.cache 14 | *.ilk 15 | *.log 16 | [Bb]in 17 | [Dd]ebug*/ 18 | *.sbr 19 | obj/ 20 | [Rr]elease*/ 21 | _ReSharper*/ 22 | .vs/ 23 | -------------------------------------------------------------------------------- /XTulator/modules/audio/sdlaudio.h: -------------------------------------------------------------------------------- 1 | #ifndef _SDLAUDIO_H_ 2 | #define _SDLAUDIO_H_ 3 | 4 | #ifdef _WIN32 5 | #include 6 | #else 7 | #include 8 | #endif 9 | #include "../../machine.h" 10 | 11 | #define SDLAUDIO_TIMING_FAST 1 12 | #define SDLAUDIO_TIMING_NORMAL 2 13 | 14 | int sdlaudio_init(MACHINE_t* machine); 15 | void sdlaudio_generateSample(void* dummy); 16 | void sdlaudio_updateSampleTiming(); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /XTulator/memory.h: -------------------------------------------------------------------------------- 1 | #ifndef _MEMORY_H_ 2 | #define _MEMORY_H_ 3 | 4 | #include 5 | 6 | #define MEMORY_RANGE 0x100000 7 | #define MEMORY_MASK 0x0FFFFF 8 | 9 | void memory_mapRegister(uint32_t start, uint32_t len, uint8_t* readb, uint8_t* writeb); 10 | void memory_mapCallbackRegister(uint32_t start, uint32_t count, uint8_t(*readb)(void*, uint32_t), void (*writeb)(void*, uint32_t, uint8_t), void* udata); 11 | int memory_init(); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /XTulator/debuglog.h: -------------------------------------------------------------------------------- 1 | #ifndef _DEBUGLOG_H_ 2 | #define _DEBUGLOG_H_ 3 | 4 | #include 5 | 6 | /* 7 | Debug levels: 8 | 9 | 0 - No logging 10 | 1 - Errors 11 | 2 - Errors, info 12 | 3 - Errors, info, detailed debugging 13 | */ 14 | 15 | #define DEBUG_NONE 0 16 | #define DEBUG_ERROR 1 17 | #define DEBUG_INFO 2 18 | #define DEBUG_DETAIL 3 19 | 20 | void debug_log(uint8_t level, char* format, ...); 21 | void debug_setLevel(uint8_t level); 22 | void debug_init(); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /XTulator/modules/io/pcap-win32.h: -------------------------------------------------------------------------------- 1 | #ifndef _PCAP_WIN32_H_ 2 | #define _PCAP_WIN32_H_ 3 | 4 | #include "../../config.h" 5 | 6 | #ifdef USE_NE2000 7 | 8 | #include 9 | #include 10 | #include "ne2000.h" 11 | 12 | void pcap_rx_handler(u_char* param, const struct pcap_pkthdr* header, const u_char* pkt_data); 13 | void pcap_listdevs(); 14 | int pcap_init(NE2000_t* ne2000, int dev); 15 | void pcap_dispatchThread(); 16 | void pcap_txPacket(u_char* data, int len); 17 | 18 | #endif //USE_NE2000 19 | 20 | #endif //_PCAP_WIN32_H_ 21 | -------------------------------------------------------------------------------- /XTulator/chipset/i8255.h: -------------------------------------------------------------------------------- 1 | #ifndef _I8255_H_ 2 | #define _I8255_H_ 3 | 4 | #include 5 | #include "../modules/audio/pcspeaker.h" 6 | #include "../modules/input/input.h" 7 | 8 | typedef struct { 9 | uint8_t sw2; 10 | uint8_t portA; 11 | uint8_t portB; 12 | uint8_t portC; 13 | KEYSTATE_t* keystate; 14 | PCSPEAKER_t* pcspeaker; 15 | } I8255_t; 16 | 17 | uint8_t i8255_readport(I8255_t* i8255, uint16_t portnum); 18 | void i8255_writeport(I8255_t* i8255, uint16_t portnum, uint8_t value); 19 | void i8255_init(I8255_t* i8255, KEYSTATE_t* keystate, PCSPEAKER_t* pcspeaker); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /XTulator/modules/input/mouse.h: -------------------------------------------------------------------------------- 1 | #ifndef _MOUSE_H_ 2 | #define _MOUSE_H_ 3 | 4 | #include 5 | #include "../../chipset/uart.h" 6 | 7 | #define MOUSE_ACTION_MOVE 0 8 | #define MOUSE_ACTION_LEFT 1 9 | #define MOUSE_ACTION_RIGHT 2 10 | 11 | #define MOUSE_PRESSED 0 12 | #define MOUSE_UNPRESSED 1 13 | #define MOUSE_NEITHER 2 14 | 15 | #define MOUSE_BUFFER_LEN 60 16 | 17 | typedef struct { 18 | uint8_t left; 19 | uint8_t right; 20 | } MOUSE_t; 21 | 22 | void mouse_togglereset(void* dummy, uint8_t value); 23 | void mouse_action(uint8_t action, uint8_t state, int32_t xrel, int32_t yrel); 24 | void mouse_rxpoll(void* dummy); 25 | void mouse_init(UART_t* uart); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /TODO.txt: -------------------------------------------------------------------------------- 1 | --Rough notes on things still to be done-- 2 | 3 | Make configure script for Linux and MacOS. 4 | 5 | Complete TCP modem and make the code cleaner. 6 | 7 | Implement all DMA modes and fix bugs. 8 | 9 | Fix remaining bugs in EGA/VGA. 10 | 11 | Legit IDE controller and XT-IDE BIOS ROM. 12 | 13 | Legit floppy controller. 14 | 15 | MFM controller?? 16 | 17 | Sound Blaster 2.0 high speed DMA mode, and fix glitches in certain games. 18 | 19 | Make my own OPL2 code not suck so bad/be completely broken. 20 | 21 | Fix chipset bugs that causes most BIOSes to not boot. 22 | 23 | Rewrite that old IDIV code in the CPU, it can be refactored to be much more simple and efficient. 24 | -------------------------------------------------------------------------------- /XTulator/modules/video/sdlconsole.h: -------------------------------------------------------------------------------- 1 | #ifndef _SDLCONSOLE_H_ 2 | #define _SDLCONSOLE_H_ 3 | 4 | #ifdef _WIN32 5 | #include 6 | #else 7 | #include 8 | #endif 9 | 10 | #define SDLCONSOLE_EVENT_NONE 0 11 | #define SDLCONSOLE_EVENT_KEY 1 12 | #define SDLCONSOLE_EVENT_QUIT 2 13 | #define SDLCONSOLE_EVENT_DEBUG_1 3 14 | #define SDLCONSOLE_EVENT_DEBUG_2 4 15 | 16 | int sdlconsole_init(char *title); 17 | void sdlconsole_blit(uint32_t* pixels, int w, int h, int stride); 18 | int sdlconsole_loop(); 19 | uint8_t sdlconsole_getScancode(); 20 | uint8_t sdlconsole_translateScancode(SDL_Keycode keyval); 21 | int sdlconsole_setWindow(int w, int h); 22 | void sdlconsole_setTitle(char* title); 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /XTulator/modules/audio/pcspeaker.h: -------------------------------------------------------------------------------- 1 | #ifndef _PCSPEAKER_H_ 2 | #define _PCSPEAKER_H_ 3 | 4 | #include 5 | 6 | #define PC_SPEAKER_GATE_DIRECT 0 7 | #define PC_SPEAKER_GATE_TIMER2 1 8 | 9 | #define PC_SPEAKER_USE_DIRECT 0 10 | #define PC_SPEAKER_USE_TIMER2 1 11 | 12 | #define PC_SPEAKER_MOVEMENT 800 13 | 14 | typedef struct { 15 | uint8_t pcspeaker_gateSelect; 16 | uint8_t pcspeaker_gate[2]; 17 | int16_t pcspeaker_amplitude; 18 | } PCSPEAKER_t; 19 | 20 | void pcspeaker_setGateState(PCSPEAKER_t* spk, uint8_t gate, uint8_t value); 21 | void pcspeaker_selectGate(PCSPEAKER_t* spk, uint8_t value); 22 | int16_t pcspeaker_getSample(PCSPEAKER_t* spk); 23 | void pcspeaker_init(PCSPEAKER_t* spk); 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /XTulator/modules/disk/biosdisk.h: -------------------------------------------------------------------------------- 1 | #ifndef _BIOSDISK_H_ 2 | #define _BIOSDISK_H_ 3 | 4 | #include 5 | #include 6 | #include "../../cpu/cpu.h" 7 | 8 | typedef struct { 9 | FILE* diskfile; 10 | uint32_t filesize; 11 | uint16_t cyls; 12 | uint16_t sects; 13 | uint16_t heads; 14 | uint8_t inserted; 15 | char* filename; 16 | } DISK_t; 17 | 18 | uint8_t biosdisk_insert(CPU_t* cpu, uint8_t drivenum, char* filename); 19 | void biosdisk_eject(CPU_t* cpu, uint8_t drivenum); 20 | void biosdisk_int13h(CPU_t* cpu, uint8_t intnum); 21 | void biosdisk_int19h(CPU_t* cpu, uint8_t intnum); 22 | uint8_t biosdisk_gethdcount(); 23 | void biosdisk_init(CPU_t* cpu); 24 | 25 | extern uint8_t bootdrive; 26 | extern DISK_t biosdisk[4]; 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /XTulator/ports.h: -------------------------------------------------------------------------------- 1 | #ifndef _PORTS_H_ 2 | #define _PORTS_H_ 3 | 4 | #include 5 | 6 | #define PORTS_COUNT 0x1000 7 | 8 | extern uint8_t(*ports_cbReadB[PORTS_COUNT])(void* udata, uint32_t portnum); 9 | extern uint16_t(*ports_cbReadW[PORTS_COUNT])(void* udata, uint32_t portnum); 10 | extern void (*ports_cbWriteB[PORTS_COUNT])(void* udata, uint32_t portnum, uint8_t value); 11 | extern void (*ports_cbWriteW[PORTS_COUNT])(void* udata, uint32_t portnum, uint16_t value); 12 | extern void* ports_udata[PORTS_COUNT]; 13 | 14 | void ports_cbRegister(uint32_t start, uint32_t count, uint8_t(*readb)(void*, uint32_t), uint16_t(*readw)(void*, uint32_t), void (*writeb)(void*, uint32_t, uint8_t), void (*writew)(void*, uint32_t, uint16_t), void* udata); 15 | void ports_init(); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /XTulator/timing.h: -------------------------------------------------------------------------------- 1 | #ifndef _TIMING_H_ 2 | #define _TIMING_H_ 3 | 4 | #include 5 | 6 | typedef struct TIMER_s { 7 | uint64_t interval; 8 | uint64_t previous; 9 | uint8_t enabled; 10 | void (*callback)(void*); 11 | void* data; 12 | } TIMER; 13 | 14 | #define TIMING_ENABLED 1 15 | #define TIMING_DISABLED 0 16 | #define TIMING_ERROR 0xFFFFFFFF 17 | 18 | #define TIMING_RINGSIZE 1024 19 | 20 | int timing_init(); 21 | void timing_loop(); 22 | uint32_t timing_addTimer(void* callback, void* data, double frequency, uint8_t enabled); 23 | void timing_updateIntervalFreq(uint32_t tnum, double frequency); 24 | void timing_updateInterval(uint32_t tnum, uint64_t interval); 25 | void timing_speedTest(); 26 | void timing_timerEnable(uint32_t tnum); 27 | void timing_timerDisable(uint32_t tnum); 28 | uint64_t timing_getFreq(); 29 | uint64_t timing_getCur(); 30 | 31 | extern uint64_t timing_cur; 32 | extern uint64_t timing_freq; 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /XTulator/modules/audio/blaster.h: -------------------------------------------------------------------------------- 1 | #ifndef _BLASTER_H_ 2 | #define _BLASTER_H_ 3 | 4 | #include 5 | #include "../../chipset/i8237.h" 6 | #include "../../chipset/i8259.h" 7 | 8 | typedef struct { 9 | I8237_t* i8237; 10 | I8259_t* i8259; 11 | uint8_t dspenable; 12 | int16_t sample; 13 | uint8_t readbuf[16]; 14 | uint8_t readlen; 15 | uint8_t readready; 16 | uint8_t writebuf; 17 | uint8_t timeconst; 18 | double samplerate; 19 | uint32_t timer; 20 | uint32_t dmalen; 21 | uint8_t dmachan; 22 | uint8_t irq; 23 | uint8_t lastcmd; 24 | uint8_t writehilo; 25 | uint32_t dmacount; 26 | uint8_t autoinit; 27 | uint8_t testreg; 28 | uint8_t silencedsp; 29 | uint8_t dorecord; 30 | uint8_t activedma; 31 | } BLASTER_t; 32 | 33 | void blaster_write(BLASTER_t* blaster, uint16_t addr, uint8_t value); 34 | uint8_t blaster_read(BLASTER_t* blaster, uint16_t addr); 35 | int16_t blaster_getSample(BLASTER_t* blaster); 36 | void blaster_init(BLASTER_t* blaster, I8237_t* i8237, I8259_t* i8259, uint16_t base, uint8_t dma, uint8_t irq); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /XTulator/modules/audio/opl2.h: -------------------------------------------------------------------------------- 1 | #ifndef _OPL2_H_ 2 | #define _OPL2_H_ 3 | 4 | #include 5 | 6 | #define VOLUME_CONST 1.2 7 | 8 | typedef struct { 9 | uint8_t chan; 10 | uint8_t op; 11 | void* opl2; 12 | } OPL2CB_t; 13 | 14 | typedef struct { 15 | uint8_t addr; 16 | uint8_t data[0x100]; 17 | struct { 18 | uint16_t fnum; 19 | uint8_t octave; 20 | double frequency; 21 | uint8_t on; 22 | } chan[9]; 23 | struct { 24 | uint32_t timer; 25 | double amplitude; 26 | double envelope; 27 | double sample; 28 | double lastsine; 29 | uint8_t volume; 30 | uint8_t inattack; 31 | uint8_t attackval; 32 | uint8_t decayval; 33 | uint8_t waveform; 34 | uint8_t sustain; 35 | uint8_t sustainlevel; 36 | uint8_t usevibrato; 37 | uint8_t usetremolo; 38 | uint32_t tick; 39 | OPL2CB_t opdata; 40 | } oper[22]; 41 | } OPL2_t; 42 | 43 | void opl2_write(OPL2_t* opl2, uint16_t portnum, uint8_t value); 44 | uint8_t opl2_read(OPL2_t* opl2, uint16_t portnum); 45 | int16_t opl2_generateSample(OPL2_t* opl2); 46 | void opl2_tickOperator(OPL2CB_t* opl2cb); 47 | void opl2_init(OPL2_t* opl2); 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /XTulator/chipset/i8237.h: -------------------------------------------------------------------------------- 1 | #ifndef _I8237_H_ 2 | #define _I8237_H_ 3 | 4 | #include 5 | #include "../cpu/cpu.h" 6 | 7 | typedef struct I8237_s { 8 | struct { 9 | uint32_t page; 10 | uint32_t addr; 11 | uint32_t reloadaddr; 12 | uint32_t addrinc; 13 | uint16_t count; 14 | uint16_t reloadcount; 15 | uint8_t autoinit; 16 | uint8_t mode; 17 | uint8_t enable; 18 | uint8_t masked; 19 | uint8_t dreq; 20 | uint8_t terminal; 21 | uint8_t operation; 22 | } chan[4]; 23 | uint8_t flipflop; 24 | uint8_t tempreg; 25 | uint8_t memtomem; 26 | CPU_t* cpu; 27 | } I8237_t; 28 | 29 | #define DMA_MODE_DEMAND 0 30 | #define DMA_MODE_SINGLE 1 31 | #define DMA_MODE_BLOCK 2 32 | #define DMA_MODE_CASCADE 3 33 | 34 | #define DMA_OP_VERIFY 0 35 | #define DMA_OP_WRITEMEM 1 36 | #define DMA_OP_READMEM 2 37 | 38 | void i8237_writeport(I8237_t* i8237, uint16_t addr, uint8_t value); 39 | uint8_t i8237_readport(I8237_t* i8237, uint16_t addr); 40 | uint8_t i8237_read(I8237_t* i8237, uint8_t ch); 41 | void i8237_write(I8237_t* i8237, uint8_t ch, uint8_t value); 42 | void i8237_init(I8237_t* i8237, CPU_t* cpu); 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /XTulator/modules/io/tcpmodem.h: -------------------------------------------------------------------------------- 1 | #ifndef _TCPMODEM_H_ 2 | #define _TCPMODEM_H_ 3 | 4 | #include "../../config.h" 5 | 6 | #ifdef ENABLE_TCP_MODEM 7 | #include 8 | #include "../../chipset/uart.h" 9 | 10 | #ifdef _WIN32 11 | #include 12 | #include 13 | 14 | typedef struct { 15 | uint8_t escaped; 16 | uint8_t livesocket; 17 | uint8_t listening; 18 | uint8_t ringing; 19 | uint8_t ringstate; 20 | uint32_t ringtimer; 21 | uint16_t listenport; 22 | uint8_t echocmd; 23 | char rxbuf[1024]; //used only in offline mode 24 | char txbuf[1024]; //used only in offline mode 25 | uint16_t rxpos; 26 | uint16_t txpos; 27 | char lasttx[3]; 28 | WSADATA wsa; 29 | SOCKET socket; 30 | SOCKET serversocket; 31 | SOCKADDR_IN server; 32 | UART_t* uart; 33 | } TCPMODEM_t; 34 | 35 | int tcpmodem_connect(TCPMODEM_t* tcpmodem, char* host, uint16_t port); 36 | void tcpmodem_offline(TCPMODEM_t* tcpmodem); 37 | void tcpmodem_rxpoll(TCPMODEM_t* tcpmodem); 38 | void tcpmodem_tx(TCPMODEM_t* tcpmodem, uint8_t value); 39 | int tcpmodem_init(TCPMODEM_t* tcpmodem, UART_t* uart, uint16_t port); 40 | 41 | #else 42 | 43 | #endif 44 | 45 | #endif 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /XTulator/menus.h: -------------------------------------------------------------------------------- 1 | #ifndef _MENUS_H_ 2 | #define MENUS_H_ 3 | 4 | #ifdef _WIN32 5 | #include 6 | #include 7 | #include "machine.h" 8 | 9 | #define MENUS_FUNCTION 0 10 | #define MENUS_SUBMENU 1 11 | #define MENUS_SEPARATOR 2 12 | 13 | #define MENUS_DISABLED 0 14 | #define MENUS_HIDDEN 1 15 | #define MENUS_ENABLED 2 16 | 17 | typedef struct { 18 | wchar_t* title; 19 | uint8_t enabled; 20 | uint8_t type; 21 | void (*function)(); 22 | } MENU_t; 23 | 24 | typedef struct { 25 | wchar_t* title; 26 | MENU_t* menu; 27 | } MENUBAR_t; 28 | 29 | int menus_init(HWND hwnd); 30 | void menus_setMachine(MACHINE_t* machine); 31 | void menus_exit(); 32 | void menus_openFloppyFile(uint8_t disk); 33 | void menus_changeFloppy0(); 34 | void menus_changeFloppy1(); 35 | void menus_ejectFloppy0(); 36 | void menus_ejectFloppy1(); 37 | void menus_insertHard0(); 38 | void menus_insertHard1(); 39 | void menus_setBootFloppy0(); 40 | void menus_setBootHard0(); 41 | void menus_reset(); 42 | void menus_speed477(); 43 | void menus_speed8(); 44 | void menus_speed10(); 45 | void menus_speed16(); 46 | void menus_speed25(); 47 | void menus_speed50(); 48 | void menus_speedunlimited(); 49 | 50 | #endif //_WIN32 51 | 52 | #endif //_MENUS_H_ 53 | -------------------------------------------------------------------------------- /XTulator/chipset/uart.h: -------------------------------------------------------------------------------- 1 | #ifndef _UART_H_ 2 | #define _UART_H_ 3 | 4 | #include 5 | #include "i8259.h" 6 | 7 | #define UART_IRQ_MSR_ENABLE 0x08 8 | #define UART_IRQ_LSR_ENABLE 0x04 9 | #define UART_IRQ_TX_ENABLE 0x02 10 | #define UART_IRQ_RX_ENABLE 0x01 11 | 12 | #define UART_PENDING_RX 0x01 13 | #define UART_PENDING_TX 0x02 14 | #define UART_PENDING_MSR 0x04 15 | #define UART_PENDING_LSR 0x08 16 | 17 | typedef struct { 18 | uint8_t rx; 19 | uint8_t tx; 20 | uint8_t rxnew; 21 | uint8_t dlab; 22 | uint8_t ien; //interrupt enable register 23 | uint8_t iir; //interrupt identification register 24 | uint8_t lcr; //line control register 25 | uint8_t mcr; //modem control register 26 | uint8_t lsr; //line status register 27 | uint8_t msr; //modem status register 28 | uint8_t lastmsr; //to calculate delta bits 29 | uint8_t scratch; 30 | uint16_t divisor; 31 | uint8_t irq; 32 | uint8_t pendirq; 33 | void* udata; 34 | void* udata2; 35 | void (*txCb)(void*, uint8_t); 36 | void (*mcrCb)(void*, uint8_t); 37 | I8259_t* i8259; 38 | } UART_t; 39 | 40 | void uart_writeport(UART_t* uart, uint16_t addr, uint8_t value); 41 | uint8_t uart_readport(UART_t* uart, uint16_t addr); 42 | void uart_rxdata(UART_t* uart, uint8_t value); 43 | void uart_init(UART_t* uart, I8259_t* i8259, uint16_t base, uint8_t irq, void (*tx)(void*, uint8_t), void* udata, void (*mcr)(void*, uint8_t), void* udata2); 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /XTulator/config.h: -------------------------------------------------------------------------------- 1 | #ifndef _CONFIG_H_ 2 | #define _CONFIG_H_ 3 | 4 | #include 5 | 6 | #define STR_TITLE "XTulator" 7 | #define STR_VERSION "0.24.5.24" 8 | 9 | //#define DEBUG_DMA 10 | //#define DEBUG_VGA 11 | //#define DEBUG_CGA 12 | //#define DEBUG_PIT 13 | //#define DEBUG_PIC 14 | //#define DEBUG_PPI 15 | //#define DEBUG_UART 16 | //#define DEBUG_TCPMODEM 17 | //#define DEBUG_PCSPEAKER 18 | //#define DEBUG_MEMORY 19 | //#define DEBUG_PORTS 20 | //#define DEBUG_TIMING 21 | //#define DEBUG_OPL2 22 | //#define DEBUG_BLASTER 23 | //#define DEBUG_FDC 24 | //#define DEBUG_NE2000 25 | //#define DEBUG_PCAP 26 | 27 | #define USE_DISK_HLE 28 | #define USE_NUKED_OPL 29 | #define USE_NE2000 30 | 31 | #ifdef _WIN32 32 | #define ENABLE_TCP_MODEM 33 | #endif 34 | 35 | #define VIDEO_CARD_MDA 0 36 | #define VIDEO_CARD_CGA 1 37 | #define VIDEO_CARD_EGA 2 38 | #define VIDEO_CARD_VGA 3 39 | 40 | #define SAMPLE_RATE 48000 41 | #define SAMPLE_BUFFER 4800 42 | 43 | #ifdef _WIN32 44 | #define FUNC_INLINE __forceinline 45 | #else 46 | #define FUNC_INLINE __attribute__((always_inline)) 47 | #endif 48 | 49 | #ifndef _WIN32 50 | #define _stricmp strcasecmp 51 | #endif 52 | 53 | extern volatile uint8_t running; 54 | extern uint8_t videocard, showMIPS; 55 | extern double speedarg; 56 | extern volatile double speed; 57 | extern uint32_t baudrate, ramsize; 58 | extern char* usemachine; 59 | extern uint8_t bootdrive; 60 | 61 | void setspeed(double mhz); 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /XTulator/cpu/cpuconf.h: -------------------------------------------------------------------------------- 1 | /* 2 | XTulator: A portable, open-source 80186 PC emulator. 3 | Copyright (C)2020 Mike Chambers 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | //Be sure to only define ONE of the CPU_* options at any given time, or you will likely get some unexpected/bad results! 21 | 22 | //#define CPU_8086 23 | //#define CPU_186 24 | #define CPU_V20 25 | //#define CPU_286 26 | 27 | #if defined(CPU_8086) 28 | #define CPU_CLEAR_ZF_ON_MUL 29 | #define CPU_ALLOW_POP_CS 30 | #else 31 | #define CPU_ALLOW_ILLEGAL_OP_EXCEPTION 32 | #define CPU_LIMIT_SHIFT_COUNT 33 | #endif 34 | 35 | #if defined(CPU_V20) 36 | #define CPU_NO_SALC 37 | #endif 38 | 39 | #if defined(CPU_286) || defined(CPU_386) 40 | #define CPU_286_STYLE_PUSH_SP 41 | #else 42 | #define CPU_SET_HIGH_FLAGS 43 | #endif 44 | -------------------------------------------------------------------------------- /XTulator.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30114.105 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "XTulator", "XTulator\XTulator.vcxproj", "{35DA3EDE-01B9-4EDC-937C-27187E9074F0}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {35DA3EDE-01B9-4EDC-937C-27187E9074F0}.Debug|x64.ActiveCfg = Debug|x64 17 | {35DA3EDE-01B9-4EDC-937C-27187E9074F0}.Debug|x64.Build.0 = Debug|x64 18 | {35DA3EDE-01B9-4EDC-937C-27187E9074F0}.Debug|x86.ActiveCfg = Debug|Win32 19 | {35DA3EDE-01B9-4EDC-937C-27187E9074F0}.Debug|x86.Build.0 = Debug|Win32 20 | {35DA3EDE-01B9-4EDC-937C-27187E9074F0}.Release|x64.ActiveCfg = Release|x64 21 | {35DA3EDE-01B9-4EDC-937C-27187E9074F0}.Release|x64.Build.0 = Release|x64 22 | {35DA3EDE-01B9-4EDC-937C-27187E9074F0}.Release|x86.ActiveCfg = Release|Win32 23 | {35DA3EDE-01B9-4EDC-937C-27187E9074F0}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {B62681F7-6BD0-4BED-8737-8D1175D1E188} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /XTulator/debuglog.c: -------------------------------------------------------------------------------- 1 | /* 2 | XTulator: A portable, open-source 80186 PC emulator. 3 | Copyright (C)2020 Mike Chambers 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | #include "config.h" 21 | #include 22 | #include 23 | #include 24 | #include "debuglog.h" 25 | 26 | uint8_t debug_level = DEBUG_INFO; 27 | 28 | void debug_log(uint8_t level, char* format, ...) { 29 | va_list argptr; 30 | va_start(argptr, format); 31 | if (level > debug_level) { 32 | va_end(argptr); 33 | return; 34 | } 35 | vfprintf(stderr, format, argptr); 36 | fflush(stderr); 37 | va_end(argptr); 38 | } 39 | 40 | void debug_setLevel(uint8_t level) { 41 | if (level > DEBUG_DETAIL) { 42 | return; 43 | } 44 | debug_level = level; 45 | } 46 | 47 | void debug_init() { 48 | //TODO: Maybe allow initializing this with file output rather than always using stderr. Or maybe remove this, I don't know... 49 | } 50 | -------------------------------------------------------------------------------- /XTulator/modules/video/cga.h: -------------------------------------------------------------------------------- 1 | #ifndef _CGA_H_ 2 | #define _CGA_H_ 3 | 4 | #include 5 | #include "../../cpu/cpu.h" 6 | 7 | extern const uint8_t cga_palette[16][3]; 8 | 9 | int cga_init(); 10 | void cga_update(uint32_t start_x, uint32_t start_y, uint32_t end_x, uint32_t end_y); 11 | void cga_writeport(void* dummy, uint16_t port, uint8_t value); 12 | uint8_t cga_readport(void* dummy, uint16_t port); 13 | void cga_blinkCallback(void* dummy); 14 | void cga_scanlineCallback(void* dummy); 15 | void cga_renderThread(void* cpu); 16 | void cga_writememory(void* dummy, uint32_t addr, uint8_t value); 17 | uint8_t cga_readmemory(void* dummy, uint32_t addr); 18 | void cga_drawCallback(void* dummy); 19 | 20 | //#define cga_color(c) ((uint32_t)cga_palette[c][0] | ((uint32_t)cga_palette[c][1]<<8) | ((uint32_t)cga_palette[c][2]<<16)) 21 | #define cga_color(c) ((uint32_t)cga_palette[c][2] | ((uint32_t)cga_palette[c][1]<<8) | ((uint32_t)cga_palette[c][0]<<16)) 22 | 23 | #define CGA_BLACK 0 24 | #define CGA_BLUE 1 25 | #define CGA_GREEN 2 26 | #define CGA_CYAN 3 27 | #define CGA_RED 4 28 | #define CGA_MAGENTA 5 29 | #define CGA_BROWN 6 30 | #define CGA_LIGHT_GRAY 7 31 | #define CGA_DARK_GRAY 8 32 | #define CGA_LIGHT_BLUE 9 33 | #define CGA_LIGHT_GREEN 10 34 | #define CGA_LIGHT_CYAN 11 35 | #define CGA_LIGHT_RED 12 36 | #define CGA_LIGHT_MAGENTA 13 37 | #define CGA_YELLOW 14 38 | #define CGA_WHITE 15 39 | 40 | #define CGA_REG_DATA_CURSOR_BEGIN 0x0A 41 | #define CGA_REG_DATA_CURSOR_END 0x0B 42 | 43 | #define CGA_MODE_TEXT_40X25 0 44 | #define CGA_MODE_TEXT_80X25 1 45 | #define CGA_MODE_GRAPHICS_LO 2 46 | #define CGA_MODE_GRAPHICS_HI 3 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /XTulator/modules/video/vga.h: -------------------------------------------------------------------------------- 1 | #ifndef _VGA_H_ 2 | #define _VGA_H_ 3 | 4 | #include 5 | #include "../../cpu/cpu.h" 6 | 7 | typedef struct { 8 | uint8_t state; 9 | uint8_t index; 10 | uint8_t step; 11 | uint8_t pal[256][3]; 12 | } VGADAC_t; 13 | 14 | extern uint8_t vga_palette[256][3]; 15 | extern volatile double vga_lockFPS; 16 | 17 | int vga_init(); 18 | void vga_updateScanlineTiming(); 19 | void vga_update(uint32_t start_x, uint32_t start_y, uint32_t end_x, uint32_t end_y); 20 | void vga_writeport(void* dummy, uint16_t port, uint8_t value); 21 | uint8_t vga_readport(void* dummy, uint16_t port); 22 | void vga_blinkCallback(void* dummy); 23 | void vga_hblankCallback(void* dummy); 24 | void vga_hblankEndCallback(void* dummy); 25 | void vga_drawCallback(void* dummy); 26 | void vga_renderThread(void* cpu); 27 | void vga_writememory(void* dummy, uint32_t addr, uint8_t value); 28 | uint8_t vga_readmemory(void* dummy, uint32_t addr); 29 | void vga_dumpregs(); 30 | 31 | //#define cga_color(c) ((uint32_t)cga_palette[c][0] | ((uint32_t)cga_palette[c][1]<<8) | ((uint32_t)cga_palette[c][2]<<16)) 32 | #define vga_color(c) ((uint32_t)vga_palette[c][2] | ((uint32_t)vga_palette[c][1]<<8) | ((uint32_t)vga_palette[c][0]<<16)) 33 | 34 | #define vga_dorotate(v) ((uint8_t)((v >> vga_rotate) | (v << (8 - vga_rotate)))) 35 | 36 | #define VGA_DAC_MODE_READ 0x00 37 | #define VGA_DAC_MODE_WRITE 0x03 38 | 39 | #define VGA_REG_DATA_CURSOR_BEGIN 0x0A 40 | #define VGA_REG_DATA_CURSOR_END 0x0B 41 | 42 | #define VGA_MODE_TEXT 0 43 | #define VGA_MODE_GRAPHICS_8BPP 1 44 | #define VGA_MODE_GRAPHICS_4BPP 2 45 | #define VGA_MODE_GRAPHICS_2BPP 3 46 | #define VGA_MODE_GRAPHICS_1BPP 4 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /XTulator/chipset/i8259.h: -------------------------------------------------------------------------------- 1 | /* 2 | XTulator: A portable, open-source 80186 PC emulator. 3 | Copyright (C)2020 Mike Chambers 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | #ifndef _I8259_H_ 21 | #define _I8259_H_ 22 | 23 | #include 24 | 25 | typedef struct { 26 | uint8_t imr; //mask register 27 | uint8_t irr; //request register 28 | uint8_t isr; //service register 29 | uint8_t icwstep; //used during initialization to keep track of which ICW we're at 30 | uint8_t icw[5]; 31 | uint8_t ocw[5]; 32 | uint8_t intoffset; //interrupt vector offset 33 | uint8_t priority; //which IRQ has highest priority 34 | uint8_t autoeoi; //automatic EOI mode 35 | uint8_t readmode; //remember what to return on read register from OCW3 36 | uint8_t vector; 37 | uint8_t lastintr; 38 | uint8_t enabled; 39 | } I8259_t; 40 | 41 | void i8259_init(I8259_t* i8259); 42 | void i8259_doirq(I8259_t* i8259, uint8_t irqnum); 43 | uint8_t i8259_nextintr(I8259_t* i8259); 44 | void i8259_write(I8259_t* i8259, uint16_t portnum, uint8_t value); 45 | uint8_t i8259_read(I8259_t* i8259, uint16_t portnum); 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /XTulator/utility.c: -------------------------------------------------------------------------------- 1 | /* 2 | XTulator: A portable, open-source 80186 PC emulator. 3 | Copyright (C)2020 Mike Chambers 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #ifdef _WIN32 24 | #include 25 | #else 26 | #include 27 | #include 28 | #endif 29 | #include "config.h" 30 | #include "memory.h" 31 | #include "debuglog.h" 32 | 33 | int utility_loadFile(uint8_t* dst, size_t len, char* srcfile) { 34 | FILE* file; 35 | if (dst == NULL) { 36 | return -1; 37 | } 38 | 39 | file = fopen(srcfile, "rb"); 40 | if (file == NULL) { 41 | free(dst); 42 | return -1; 43 | } 44 | if (fread(dst, 1, len, file) < len) { 45 | free(dst); 46 | fclose(file); 47 | return -1; 48 | } 49 | fclose(file); 50 | return 0; 51 | } 52 | 53 | void utility_sleep(uint32_t ms) { 54 | #ifdef _WIN32 55 | Sleep((DWORD)ms); 56 | #else 57 | int res; 58 | struct timespec ts; 59 | ts.tv_sec = 0; 60 | ts.tv_nsec = (long)ms * 1000; 61 | do { 62 | res = nanosleep(&ts, &ts); 63 | } while (res && errno == EINTR); 64 | #endif 65 | } 66 | -------------------------------------------------------------------------------- /XTulator/chipset/i8253.h: -------------------------------------------------------------------------------- 1 | /* 2 | XTulator: A portable, open-source 80186 PC emulator. 3 | Copyright (C)2020 Mike Chambers 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | #ifndef _I8253_H_ 21 | #define _I8253_H_ 22 | 23 | #include 24 | #include "i8259.h" 25 | #include "../modules/audio/pcspeaker.h" 26 | 27 | #define PIT_MODE_LATCHCOUNT 0 28 | #define PIT_MODE_LOBYTE 1 29 | #define PIT_MODE_HIBYTE 2 30 | #define PIT_MODE_TOGGLE 3 31 | 32 | typedef struct { 33 | void* i8253; 34 | I8259_t* i8259; 35 | PCSPEAKER_t* pcspeaker; 36 | } I8253CB_t; 37 | 38 | typedef struct { 39 | uint16_t chandata[3]; 40 | uint8_t accessmode[3]; 41 | uint8_t bytetoggle[3]; 42 | uint32_t effectivedata[3]; 43 | float chanfreq[3]; 44 | 45 | uint8_t active[3]; 46 | int32_t counter[3]; 47 | int32_t reload[3]; 48 | uint8_t mode[3]; 49 | uint8_t dataflipflop[3]; 50 | uint8_t bcd[3]; 51 | uint8_t rlmode[3]; 52 | uint16_t latch[3]; 53 | uint8_t out[3]; 54 | I8253CB_t cbdata; 55 | } I8253_t; 56 | 57 | void i8253_write(I8253_t* i8253, uint16_t portnum, uint8_t value); 58 | uint8_t i8253_read(I8253_t* i8253, uint16_t portnum); 59 | void i8253_init(I8253_t* i8253, I8259_t* i8259, PCSPEAKER_t* pcspeaker); 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /XTulator/modules/audio/pcspeaker.c: -------------------------------------------------------------------------------- 1 | /* 2 | XTulator: A portable, open-source 80186 PC emulator. 3 | Copyright (C)2020 Mike Chambers 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | #include "../../config.h" 21 | #include 22 | #include 23 | #include 24 | #include "pcspeaker.h" 25 | #include "../../timing.h" 26 | 27 | void pcspeaker_setGateState(PCSPEAKER_t* spk, uint8_t gate, uint8_t value) { 28 | spk->pcspeaker_gate[gate] = value; 29 | } 30 | 31 | void pcspeaker_selectGate(PCSPEAKER_t* spk, uint8_t value) { 32 | spk->pcspeaker_gateSelect = value; 33 | } 34 | 35 | void pcspeaker_callback(PCSPEAKER_t* spk) { 36 | if (spk->pcspeaker_gateSelect == PC_SPEAKER_USE_TIMER2) { 37 | if (spk->pcspeaker_gate[PC_SPEAKER_GATE_TIMER2] && spk->pcspeaker_gate[PC_SPEAKER_GATE_DIRECT]) { 38 | if (spk->pcspeaker_amplitude < 15000) { 39 | spk->pcspeaker_amplitude += PC_SPEAKER_MOVEMENT; 40 | } 41 | } 42 | else { 43 | if (spk->pcspeaker_amplitude > 0) { 44 | spk->pcspeaker_amplitude -= PC_SPEAKER_MOVEMENT; 45 | } 46 | } 47 | //pcspeaker_amplitude = 0; 48 | } 49 | else { 50 | if (spk->pcspeaker_gate[PC_SPEAKER_GATE_DIRECT]) { 51 | if (spk->pcspeaker_amplitude < 15000) { 52 | spk->pcspeaker_amplitude += PC_SPEAKER_MOVEMENT; 53 | } 54 | } 55 | else { 56 | if (spk->pcspeaker_amplitude > 0) { 57 | spk->pcspeaker_amplitude -= PC_SPEAKER_MOVEMENT; 58 | } 59 | } 60 | } 61 | if (spk->pcspeaker_amplitude > 15000) spk->pcspeaker_amplitude = 15000; 62 | if (spk->pcspeaker_amplitude < 0) spk->pcspeaker_amplitude = 0; 63 | } 64 | 65 | void pcspeaker_init(PCSPEAKER_t* spk) { 66 | memset(spk, 0, sizeof(PCSPEAKER_t)); 67 | spk->pcspeaker_gateSelect = PC_SPEAKER_GATE_DIRECT; 68 | timing_addTimer(pcspeaker_callback, spk, SAMPLE_RATE, TIMING_ENABLED); 69 | } 70 | 71 | int16_t pcspeaker_getSample(PCSPEAKER_t* spk) { 72 | //return 0; //PC speaker is really broken, so silence for now 73 | return spk->pcspeaker_amplitude; 74 | } 75 | -------------------------------------------------------------------------------- /XTulator/modules/input/sdlkeys.h: -------------------------------------------------------------------------------- 1 | #ifndef _SDLKEYS_H_ 2 | #define _SDLKEYS_H_ 3 | 4 | #include 5 | #ifdef _WIN32 6 | #include 7 | #else 8 | #include 9 | #endif 10 | 11 | const uint32_t sdlconsole_translateMatrix[95][2] = { 12 | { SDLK_ESCAPE, 0x01 }, 13 | { SDLK_1, 0x02 }, 14 | { SDLK_2, 0x03 }, 15 | { SDLK_3, 0x04 }, 16 | { SDLK_4, 0x05 }, 17 | { SDLK_5, 0x06 }, 18 | { SDLK_6, 0x07 }, 19 | { SDLK_7, 0x08 }, 20 | { SDLK_8, 0x09 }, 21 | { SDLK_9, 0x0A }, 22 | { SDLK_0, 0x0B }, 23 | { SDLK_MINUS, 0x0C }, 24 | { SDLK_EQUALS, 0x0D }, 25 | { SDLK_BACKSPACE, 0x0E }, 26 | { SDLK_TAB, 0x0F }, 27 | { SDLK_q, 0x10 }, 28 | { SDLK_w, 0x11 }, 29 | { SDLK_e, 0x12 }, 30 | { SDLK_r, 0x13 }, 31 | { SDLK_t, 0x14 }, 32 | { SDLK_y, 0x15 }, 33 | { SDLK_u, 0x16 }, 34 | { SDLK_i, 0x17 }, 35 | { SDLK_o, 0x18 }, 36 | { SDLK_p, 0x19 }, 37 | { SDLK_LEFTBRACKET, 0x1A }, 38 | { SDLK_RIGHTBRACKET, 0x1B }, 39 | { SDLK_RETURN, 0x1C }, 40 | { SDLK_RETURN2, 0x1C }, 41 | { SDLK_KP_ENTER, 0x1C }, 42 | { SDLK_LCTRL, 0x1D }, 43 | { SDLK_a, 0x1E }, 44 | { SDLK_s, 0x1F }, 45 | { SDLK_d, 0x20 }, 46 | { SDLK_f, 0x21 }, 47 | { SDLK_g, 0x22 }, 48 | { SDLK_h, 0x23 }, 49 | { SDLK_j, 0x24 }, 50 | { SDLK_k, 0x25 }, 51 | { SDLK_l, 0x26 }, 52 | { SDLK_SEMICOLON, 0x27 }, 53 | { SDLK_QUOTE, 0x28 }, 54 | { SDLK_BACKQUOTE, 0x29 }, 55 | { SDLK_LSHIFT, 0x2A }, 56 | { SDLK_BACKSLASH, 0x2B }, 57 | { SDLK_z, 0x2C }, 58 | { SDLK_x, 0x2D }, 59 | { SDLK_c, 0x2E }, 60 | { SDLK_v, 0x2F }, 61 | { SDLK_b, 0x30 }, 62 | { SDLK_n, 0x31 }, 63 | { SDLK_m, 0x32 }, 64 | { SDLK_COMMA, 0x33 }, 65 | { SDLK_PERIOD, 0x34 }, 66 | { SDLK_SLASH, 0x35 }, 67 | { SDLK_RSHIFT, 0x36 }, 68 | { SDLK_KP_MULTIPLY, 0x37 }, 69 | { SDLK_LALT, 0x38 }, 70 | { SDLK_SPACE, 0x39 }, 71 | { SDLK_CAPSLOCK, 0x3A }, 72 | { SDLK_F1, 0x3B }, 73 | { SDLK_F2, 0x3C }, 74 | { SDLK_F3, 0x3D }, 75 | { SDLK_F4, 0x3E }, 76 | { SDLK_F5, 0x3F }, 77 | { SDLK_F6, 0x40 }, 78 | { SDLK_F7, 0x41 }, 79 | { SDLK_F8, 0x42 }, 80 | { SDLK_F9, 0x43 }, 81 | { SDLK_F10, 0x44 }, 82 | { SDLK_NUMLOCKCLEAR, 0x45 }, 83 | { SDLK_SCROLLLOCK, 0x46 }, 84 | { SDLK_KP_7, 0x47 }, 85 | { SDLK_HOME, 0x47 }, 86 | { SDLK_KP_8, 0x48 }, 87 | { SDLK_UP, 0x48 }, 88 | { SDLK_KP_9, 0x49 }, 89 | { SDLK_PAGEUP, 0x49 }, 90 | { SDLK_KP_MINUS, 0x4A }, 91 | { SDLK_KP_4, 0x4B }, 92 | { SDLK_LEFT, 0x4B }, 93 | { SDLK_KP_5, 0x4C }, 94 | { SDLK_KP_6, 0x4D }, 95 | { SDLK_RIGHT, 0x4D }, 96 | { SDLK_KP_PLUS, 0x4E }, 97 | { SDLK_KP_1, 0x4F }, 98 | { SDLK_END, 0x4F }, 99 | { SDLK_KP_2, 0x50 }, 100 | { SDLK_DOWN, 0x50 }, 101 | { SDLK_KP_3, 0x51 }, 102 | { SDLK_PAGEDOWN, 0x51 }, 103 | { SDLK_KP_0, 0x52 }, 104 | { SDLK_INSERT, 0x52 }, 105 | { SDLK_KP_PERIOD, 0x53 }, 106 | { SDLK_DELETE, 0x53 } 107 | }; 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /XTulator/machine.h: -------------------------------------------------------------------------------- 1 | #ifndef _MACHINE_H_ 2 | #define _MACHINE_H_ 3 | 4 | #include "config.h" 5 | #include 6 | #include "cpu/cpu.h" 7 | #include "chipset/i8259.h" 8 | #include "chipset/i8253.h" 9 | #include "chipset/i8237.h" 10 | #include "chipset/i8255.h" 11 | #include "chipset/uart.h" 12 | #include "modules/io/ne2000.h" 13 | #include "modules/io/tcpmodem.h" 14 | #include "modules/audio/opl2.h" 15 | #include "modules/audio/nukedopl.h" 16 | #include "modules/audio/blaster.h" 17 | #include "modules/audio/pcspeaker.h" 18 | #include "modules/disk/fdc.h" 19 | #include "modules/input/input.h" 20 | 21 | #define MACHINE_MEM_RAM 0 22 | #define MACHINE_MEM_ROM 1 23 | #define MACHINE_MEM_ENDLIST 2 24 | 25 | #define MACHINE_ROM_OPTIONAL 0 26 | #define MACHINE_ROM_REQUIRED 1 27 | #define MACHINE_ROM_ISNOTROM 2 28 | 29 | #define MACHINE_HW_OPL 0x0000000000000001ULL 30 | #define MACHINE_HW_BLASTER 0x0000000000000002ULL 31 | #define MACHINE_HW_UART0_NONE 0x0000000000000004ULL 32 | #define MACHINE_HW_UART0_MOUSE 0x0000000000000008ULL 33 | #define MACHINE_HW_UART0_TCPMODEM 0x0000000000000010ULL 34 | #define MACHINE_HW_UART1_NONE 0x0000000000000020ULL 35 | #define MACHINE_HW_UART1_MOUSE 0x0000000000000040ULL 36 | #define MACHINE_HW_UART1_TCPMODEM 0x0000000000000080ULL 37 | #define MACHINE_HW_RTC 0x0000000000000100ULL 38 | #define MACHINE_HW_DISK_HLE 0x0000000000000200ULL 39 | #define MACHINE_HW_NE2000 0x0000000000000400ULL 40 | 41 | //the "skip" HW flags are set in args.c to make sure machine init functions don't override explicit settings from the command line 42 | #define MACHINE_HW_SKIP_OPL 0x8000000000000000ULL 43 | #define MACHINE_HW_SKIP_BLASTER 0x4000000000000000ULL 44 | #define MACHINE_HW_SKIP_UART0 0x2000000000000000ULL 45 | #define MACHINE_HW_SKIP_UART1 0x1000000000000000ULL 46 | #define MACHINE_HW_SKIP_DISK 0x0800000000000000ULL 47 | #define MACHINE_HW_SKIP_RTC 0x0400000000000000ULL 48 | 49 | typedef struct { 50 | CPU_t CPU; 51 | I8259_t i8259; 52 | I8253_t i8253; 53 | I8237_t i8237; 54 | I8255_t i8255; 55 | UART_t UART[2]; 56 | #ifdef ENABLE_TCP_MODEM 57 | TCPMODEM_t tcpmodem[2]; 58 | #endif 59 | OPL2_t OPL2; 60 | opl3_chip OPL3; 61 | uint8_t mixOPL; 62 | BLASTER_t blaster; 63 | uint8_t mixBlaster; 64 | PCSPEAKER_t pcspeaker; 65 | #ifdef USE_NE2000 66 | NE2000_t ne2000; 67 | #endif 68 | KEYSTATE_t KeyState; 69 | FDC_t fdc; 70 | uint64_t hwflags; 71 | int pcap_if; 72 | } MACHINE_t; 73 | 74 | typedef struct { 75 | uint8_t memtype; 76 | uint32_t start; 77 | uint32_t size; 78 | uint8_t required; 79 | char* filename; 80 | } MACHINEMEM_t; 81 | 82 | typedef struct { 83 | char* id; 84 | char* description; 85 | int (*init)(MACHINE_t* machine); 86 | uint8_t video; 87 | double speed; 88 | uint64_t hwflags; 89 | } MACHINEDEF_t; 90 | 91 | int machine_init_generic_xt(MACHINE_t* machine); 92 | int machine_init(MACHINE_t* machine, char* id); 93 | void machine_list(); 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /XTulator/modules/input/mouse.c: -------------------------------------------------------------------------------- 1 | /* 2 | XTulator: A portable, open-source 80186 PC emulator. 3 | Copyright (C)2020 Mike Chambers 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | /* 21 | Microsoft-compatible serial mouse 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include "../../config.h" 28 | #include "../../debuglog.h" 29 | #include "../../ports.h" 30 | #include "../../chipset/uart.h" 31 | #include "mouse.h" 32 | 33 | MOUSE_t mouse_state; 34 | UART_t* mouse_uart = NULL; 35 | uint8_t mouse_buf[MOUSE_BUFFER_LEN]; //room for six events 36 | uint8_t mouse_bufpos = 0; 37 | uint8_t mouse_lasttoggle = 0; 38 | 39 | void mouse_addbuf(uint8_t value) { 40 | if (mouse_bufpos == MOUSE_BUFFER_LEN) return; 41 | 42 | mouse_buf[mouse_bufpos++] = value; 43 | } 44 | 45 | void mouse_togglereset(void* dummy, uint8_t value) { //reset mouse, allows detection, is a callback for the UART module 46 | if ((mouse_lasttoggle != 0x03) && ((value & 0x03) == 0x03)) { 47 | mouse_bufpos = 0; 48 | mouse_addbuf('M'); 49 | //printf("toggle DTR "); 50 | } 51 | mouse_lasttoggle = value & 0x03; 52 | } 53 | 54 | void mouse_action(uint8_t action, uint8_t state, int32_t xrel, int32_t yrel) { 55 | if (mouse_uart == NULL) return; 56 | switch (action) { 57 | case MOUSE_ACTION_MOVE: 58 | //printf("X: %ld, Y: %ld\r\n", xrel, yrel); 59 | break; 60 | case MOUSE_ACTION_LEFT: 61 | mouse_state.left = (state == MOUSE_PRESSED) ? 1 : 0; 62 | break; 63 | case MOUSE_ACTION_RIGHT: 64 | mouse_state.right = (state == MOUSE_PRESSED) ? 1 : 0; 65 | break; 66 | } 67 | 68 | mouse_addbuf(0x40 | ((yrel & 0xC0) >> 4) | ((xrel & 0xC0) >> 6) | (mouse_state.left ? 0x20 : 0x00) | (mouse_state.right ? 0x10 : 0x00)); 69 | mouse_addbuf(xrel & 0x3F); 70 | mouse_addbuf(yrel & 0x3F); 71 | } 72 | 73 | void mouse_rxpoll(void* dummy) { 74 | if (mouse_uart == NULL) return; 75 | if (mouse_uart->rxnew) return; 76 | if (mouse_bufpos == 0) return; 77 | 78 | uart_rxdata(mouse_uart, mouse_buf[0]); 79 | memmove(mouse_buf, mouse_buf + 1, MOUSE_BUFFER_LEN - 1); 80 | mouse_bufpos--; 81 | } 82 | 83 | void mouse_init(UART_t* uart) { 84 | debug_log(DEBUG_INFO, "[MOUSE] Initializing Microsoft-compatible serial mouse\r\n"); 85 | mouse_uart = uart; 86 | } 87 | -------------------------------------------------------------------------------- /XTulator/modules/disk/fdc.h: -------------------------------------------------------------------------------- 1 | #ifndef _FDC_H_ 2 | #define _FDC_H_ 3 | 4 | #include 5 | #include 6 | #include "../../cpu/cpu.h" 7 | #include "../../chipset/i8259.h" 8 | #include "../../chipset/i8237.h" 9 | 10 | #define FDC_FIFO_LEN 1024 11 | 12 | #define FDC_CMD_READ_TRACK 2 13 | #define FDC_CMD_SPECIFY 3 14 | #define FDC_CMD_SENSE_DRIVE_STATUS 4 15 | #define FDC_CMD_WRITE_DATA 5 16 | #define FDC_CMD_READ_DATA 6 17 | #define FDC_CMD_RECALIBRATE 7 18 | #define FDC_CMD_SENSE_INTERRUPT 8 19 | #define FDC_CMD_WRITE_DELETED_DATA 9 20 | #define FDC_CMD_READ_ID 10 21 | #define FDC_CMD_READ_DELETED_DATA 12 22 | #define FDC_CMD_FORMAT_TRACK 13 23 | #define FDC_CMD_SEEK 15 24 | 25 | #define FDC_ST0_HD 0x04 26 | #define FDC_ST0_NR 0x08 27 | #define FDC_ST0_UC 0x10 28 | #define FDC_ST0_SE 0x20 29 | #define FDC_ST0_INT_NORMAL 0x00 30 | #define FDC_ST0_INT_ABNORMAL 0x40 31 | #define FDC_ST0_INT_INVALID 0x80 32 | #define FDC_ST0_INT_ABNORMAL_POLL 0xC0 33 | 34 | #define FDC_ST1_NID 0x01 35 | #define FDC_ST1_NW 0x02 36 | #define FDC_ST1_NDAT 0x04 37 | #define FDC_ST1_TO 0x10 38 | #define FDC_ST1_DE 0x20 39 | #define FDC_ST1_EN 0x80 40 | 41 | #define FDC_ST2_NDAM 0x01 42 | #define FDC_ST2_BCYL 0x02 43 | #define FDC_ST2_SERR 0x04 44 | #define FDC_ST2_SEQ 0x08 45 | #define FDC_ST2_WCYL 0x10 46 | #define FDC_ST2_CRCE 0x20 47 | #define FDC_ST2_DADM 0x40 48 | 49 | #define FDC_ST3_HDDR 0x04 50 | #define FDC_ST3_DSDR 0x08 51 | #define FDC_ST3_TRK0 0x10 52 | #define FDC_ST3_RDY 0x20 53 | #define FDC_ST3_WPDR 0x40 54 | #define FDC_ST3_ESIG 0x80 55 | 56 | typedef struct { 57 | uint8_t inserted; 58 | FILE* dfile; 59 | uint32_t size; 60 | uint32_t sectors; 61 | uint32_t tracks; 62 | uint32_t sides; 63 | } FDCDISK_t; 64 | 65 | typedef struct { 66 | uint32_t track; 67 | uint32_t head; 68 | uint32_t sect; 69 | uint32_t wanttrack; 70 | uint8_t seeking; 71 | uint8_t reading; 72 | uint8_t transferring; 73 | } FDCPOS_t; 74 | 75 | typedef struct { 76 | CPU_t* cpu; 77 | I8259_t* i8259; 78 | I8237_t* i8237; 79 | uint8_t irq; 80 | uint8_t dma; 81 | uint8_t reg[8]; 82 | uint8_t cmd[9]; 83 | uint8_t cmd_pos; 84 | uint8_t last_cmd; 85 | uint8_t drivenum; 86 | uint8_t motoron[4]; 87 | uint8_t datatosend; 88 | uint8_t fifo[FDC_FIFO_LEN]; 89 | uint16_t fifopos; 90 | uint16_t fifolen; 91 | uint8_t st[4]; //status registers 92 | uint8_t usedma; 93 | uint8_t busy; 94 | uint32_t timerseek; 95 | uint32_t timerread; 96 | FDCPOS_t position[4]; 97 | FDCDISK_t disk[4]; 98 | uint8_t sectbuf[512]; 99 | uint16_t sectpos; //where the CPU is during the sector read process 100 | } FDC_t; 101 | 102 | uint8_t fdc_fiforead(FDC_t* fdc); 103 | void fdc_fifoadd(FDC_t* fdc, uint8_t value); 104 | void fdc_fifoclear(FDC_t* fdc); 105 | void fdc_reset(FDC_t* fdc); 106 | int fdc_insert(FDC_t* fdc, uint8_t num, char* dfile); 107 | int fdc_init(FDC_t* fdc, CPU_t* cpu, I8259_t* i8259, I8237_t* i8237); 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /XTulator/memory.c: -------------------------------------------------------------------------------- 1 | /* 2 | XTulator: A portable, open-source 80186 PC emulator. 3 | Copyright (C)2020 Mike Chambers 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include "config.h" 24 | #include "cpu/cpu.h" 25 | #include "modules/video/cga.h" 26 | #include "modules/video/vga.h" 27 | #include "utility.h" 28 | #include "memory.h" 29 | 30 | uint8_t* memory_mapRead[MEMORY_RANGE]; 31 | uint8_t* memory_mapWrite[MEMORY_RANGE]; 32 | uint8_t (*memory_mapReadCallback[MEMORY_RANGE])(void* udata, uint32_t addr); 33 | void (*memory_mapWriteCallback[MEMORY_RANGE])(void* udata, uint32_t addr, uint8_t value); 34 | void* memory_udata[MEMORY_RANGE]; 35 | 36 | void cpu_write(CPU_t* cpu, uint32_t addr32, uint8_t value) { 37 | addr32 &= MEMORY_MASK; 38 | 39 | if (memory_mapWrite[addr32] != NULL) { 40 | *(memory_mapWrite[addr32]) = value; 41 | } 42 | else if (memory_mapWriteCallback[addr32] != NULL) { 43 | (*memory_mapWriteCallback[addr32])(memory_udata[addr32], addr32, value); 44 | } 45 | } 46 | 47 | uint8_t cpu_read(CPU_t* cpu, uint32_t addr32) { 48 | addr32 &= MEMORY_MASK; 49 | 50 | if (memory_mapRead[addr32] != NULL) { 51 | return *(memory_mapRead[addr32]); 52 | } 53 | 54 | if (memory_mapReadCallback[addr32] != NULL) { 55 | return (*memory_mapReadCallback[addr32])(memory_udata[addr32], addr32); 56 | } 57 | 58 | return 0xFF; 59 | } 60 | 61 | void memory_mapRegister(uint32_t start, uint32_t len, uint8_t* readb, uint8_t* writeb) { 62 | uint32_t i; 63 | for (i = 0; i < len; i++) { 64 | if ((start + i) >= MEMORY_RANGE) { 65 | break; 66 | } 67 | memory_mapRead[start + i] = (readb == NULL) ? NULL : readb + i; 68 | memory_mapWrite[start + i] = (writeb == NULL) ? NULL : writeb + i; 69 | } 70 | } 71 | 72 | void memory_mapCallbackRegister(uint32_t start, uint32_t count, uint8_t(*readb)(void*, uint32_t), void (*writeb)(void*, uint32_t, uint8_t), void* udata) { 73 | uint32_t i; 74 | for (i = 0; i < count; i++) { 75 | if ((start + i) >= MEMORY_RANGE) { 76 | break; 77 | } 78 | memory_mapReadCallback[start + i] = readb; 79 | memory_mapWriteCallback[start + i] = writeb; 80 | memory_udata[start + i] = udata; 81 | } 82 | } 83 | 84 | int memory_init() { 85 | uint32_t i; 86 | 87 | for (i = 0; i < MEMORY_RANGE; i++) { 88 | memory_mapRead[i] = NULL; 89 | memory_mapWrite[i] = NULL; 90 | memory_mapReadCallback[i] = NULL; 91 | memory_mapWriteCallback[i] = NULL; 92 | memory_udata[i] = NULL; 93 | } 94 | 95 | return 0; 96 | } 97 | -------------------------------------------------------------------------------- /XTulator/rtc.c: -------------------------------------------------------------------------------- 1 | /* 2 | XTulator: A portable, open-source 80186 PC emulator. 3 | Copyright (C)2020 Mike Chambers 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | /* 21 | Generic RTC interface for XT systems, works with TIMER.COM version 1.2 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include "config.h" 28 | #include "ports.h" 29 | #include "debuglog.h" 30 | 31 | #ifdef _WIN32 32 | 33 | #include 34 | 35 | uint8_t rtc_read(void* dummy, uint16_t addr) { 36 | uint8_t ret = 0xFF; 37 | SYSTEMTIME tdata; 38 | 39 | GetLocalTime(&tdata); 40 | 41 | addr &= 0x1F; 42 | switch (addr) { 43 | case 1: 44 | ret = (uint8_t)tdata.wMilliseconds / 10; 45 | break; 46 | case 2: 47 | ret = (uint8_t)tdata.wSecond; 48 | break; 49 | case 3: 50 | ret = (uint8_t)tdata.wMinute; 51 | break; 52 | case 4: 53 | ret = (uint8_t)tdata.wHour; 54 | break; 55 | case 5: 56 | ret = (uint8_t)tdata.wDayOfWeek; 57 | break; 58 | case 6: 59 | ret = (uint8_t)tdata.wDay; 60 | break; 61 | case 7: 62 | ret = (uint8_t)tdata.wMonth; 63 | break; 64 | case 9: 65 | ret = (uint8_t)tdata.wYear % 100; 66 | break; 67 | } 68 | 69 | if (ret != 0xFF) { 70 | uint8_t rh, rl; 71 | rh = (ret / 10) % 10; 72 | rl = ret % 10; 73 | ret = (rh << 4) | rl; 74 | } 75 | 76 | return ret; 77 | } 78 | 79 | #else 80 | 81 | #include 82 | 83 | uint8_t rtc_read(void* dummy, uint16_t addr) { 84 | uint8_t ret = 0xFF; 85 | struct tm tdata; 86 | 87 | time(&tdata); 88 | 89 | addr &= 0x1F; 90 | switch (addr) { 91 | case 1: 92 | ret = 0; 93 | break; 94 | case 2: 95 | ret = (uint8_t)tdata.tm_sec; 96 | break; 97 | case 3: 98 | ret = (uint8_t)tdata.tm_min; 99 | break; 100 | case 4: 101 | ret = (uint8_t)tdata.tm_hour; 102 | break; 103 | case 5: 104 | ret = (uint8_t)tdata.tm_wday; 105 | break; 106 | case 6: 107 | ret = (uint8_t)tdata.tm_mday; 108 | break; 109 | case 7: 110 | ret = (uint8_t)tdata.tm_mon; 111 | break; 112 | case 9: 113 | ret = (uint8_t)tdata.tm_year % 100; 114 | break; 115 | } 116 | 117 | if (ret != 0xFF) { 118 | uint8_t rh, rl; 119 | rh = (ret / 10) % 10; 120 | rl = ret % 10; 121 | ret = (rh << 4) | rl; 122 | } 123 | 124 | return ret; 125 | } 126 | 127 | #endif 128 | 129 | void rtc_write(void* dummy, uint16_t addr, uint8_t value) { 130 | 131 | } 132 | 133 | void rtc_init() { 134 | debug_log(DEBUG_INFO, "[RTC] Initializing real time clock\r\n"); 135 | ports_cbRegister(0x240, 0x18, (void*)rtc_read, NULL, (void*)rtc_write, NULL, NULL); 136 | } 137 | -------------------------------------------------------------------------------- /XTulator/chipset/i8255.c: -------------------------------------------------------------------------------- 1 | /* 2 | XTulator: A portable, open-source 80186 PC emulator. 3 | Copyright (C)2020 Mike Chambers 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | /* 21 | Intel 8255 Programmable Peripheral Interface (PPI) 22 | 23 | This is not complete. 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include "../config.h" 30 | #include "../timing.h" 31 | #include "../modules/audio/pcspeaker.h" 32 | #include "i8255.h" 33 | #include "../ports.h" 34 | #include "../debuglog.h" 35 | 36 | uint8_t i8255_readport(I8255_t* i8255, uint16_t portnum) { 37 | #ifdef DEBUG_PPI 38 | debug_log(DEBUG_DETAIL, "[I8255] Read port %02X\r\n", portnum); 39 | #endif 40 | portnum &= 7; 41 | switch (portnum) { 42 | case 0: 43 | return i8255->keystate->scancode; 44 | case 1: 45 | return i8255->portB; 46 | case 2: 47 | //debug_log(DEBUG_DETAIL, "read 0x62\r\n"); 48 | if (i8255->portB & 8) { 49 | return i8255->sw2 >> 4; 50 | } else { 51 | return i8255->sw2 & 0x0F; 52 | } 53 | } 54 | return 0xFF; 55 | } 56 | 57 | void i8255_writeport(I8255_t* i8255, uint16_t portnum, uint8_t value) { 58 | #ifdef DEBUG_PPI 59 | debug_log(DEBUG_DETAIL, "[I8255] Write port %02X <- %02X\r\n", portnum, value); 60 | #endif 61 | portnum &= 7; 62 | switch (portnum) { 63 | case 0: 64 | i8255->keystate->scancode = 0xAA; 65 | break; 66 | case 1: 67 | if (value & 0x01) { 68 | pcspeaker_selectGate(i8255->pcspeaker, PC_SPEAKER_USE_TIMER2); 69 | #ifdef DEBUG_PPI 70 | debug_log(DEBUG_DETAIL, "[I8255] Speaker take input from timer 2\r\n"); 71 | #endif 72 | } else { 73 | pcspeaker_selectGate(i8255->pcspeaker, PC_SPEAKER_USE_DIRECT); 74 | #ifdef DEBUG_PPI 75 | debug_log(DEBUG_DETAIL, "[I8255] Speaker take input from direct\r\n"); 76 | #endif 77 | } 78 | pcspeaker_setGateState(i8255->pcspeaker, PC_SPEAKER_GATE_DIRECT, (value >> 1) & 1); 79 | #ifdef DEBUG_PPI 80 | debug_log(DEBUG_DETAIL, "[I8255] Speaker direct value = %u\r\n", (value >> 1) & 1); 81 | #endif 82 | if ((value & 0x40) && !(i8255->portB & 0x40)) { 83 | i8255->keystate->scancode = 0xAA; 84 | #ifdef DEBUG_PPI 85 | debug_log(DEBUG_DETAIL, "[I8255] Keyboard reset\r\n"); 86 | #endif 87 | } 88 | i8255->portB = (value & 0xEF) | (i8255->portB & 0x10); 89 | break; 90 | } 91 | } 92 | 93 | void i8255_refreshToggle(I8255_t* i8255) { 94 | i8255->portB ^= 0x10; //simulate DRAM refresh toggle, many BIOSes require this... 95 | } 96 | 97 | void i8255_init(I8255_t* i8255, KEYSTATE_t* keystate, PCSPEAKER_t* pcspeaker) { 98 | memset(i8255, 0, sizeof(I8255_t)); 99 | i8255->keystate = keystate; 100 | i8255->pcspeaker = pcspeaker; 101 | 102 | if (videocard == VIDEO_CARD_VGA) { 103 | i8255->sw2 = 0x46; 104 | } 105 | else if (videocard == VIDEO_CARD_CGA) { 106 | i8255->sw2 = 0x66; 107 | } 108 | 109 | ports_cbRegister(0x60, 6, (void*)i8255_readport, NULL, (void*)i8255_writeport, NULL, i8255); 110 | timing_addTimer(i8255_refreshToggle, i8255, 66667, TIMING_ENABLED); 111 | } 112 | -------------------------------------------------------------------------------- /XTulator/ports.c: -------------------------------------------------------------------------------- 1 | /* 2 | XTulator: A portable, open-source 80186 PC emulator. 3 | Copyright (C)2020 Mike Chambers 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | #include 21 | #include 22 | #include //for rand() 23 | #include 24 | #include "config.h" 25 | #include "debuglog.h" 26 | #include "cpu/cpu.h" 27 | #include "machine.h" 28 | #include "chipset/i8259.h" 29 | #include "chipset/i8253.h" 30 | #include "chipset/i8237.h" 31 | #include "chipset/i8255.h" 32 | #include "ports.h" 33 | #include "modules/video/sdlconsole.h" 34 | #include "modules/video/cga.h" 35 | #include "modules/video/vga.h" 36 | 37 | uint8_t (*ports_cbReadB[PORTS_COUNT])(void* udata, uint32_t portnum); 38 | uint16_t (*ports_cbReadW[PORTS_COUNT])(void* udata, uint32_t portnum); 39 | void (*ports_cbWriteB[PORTS_COUNT])(void* udata, uint32_t portnum, uint8_t value); 40 | void (*ports_cbWriteW[PORTS_COUNT])(void* udata, uint32_t portnum, uint16_t value); 41 | void* ports_udata[PORTS_COUNT]; 42 | 43 | extern MACHINE_t machine; 44 | 45 | void port_write(CPU_t* cpu, uint16_t portnum, uint8_t value) { 46 | #ifdef DEBUG_PORTS 47 | debug_log(DEBUG_DETAIL, "port_write @ %03X <- %02X\r\n", portnum, value); 48 | #endif 49 | portnum &= 0x0FFF; 50 | if (portnum == 0x80) { 51 | debug_log(DEBUG_DETAIL, "Diagnostic port out: %02X\r\n", value); 52 | } 53 | if (ports_cbWriteB[portnum] != NULL) { 54 | (*ports_cbWriteB[portnum])(ports_udata[portnum], portnum, value); 55 | return; 56 | } 57 | } 58 | 59 | void port_writew(CPU_t* cpu, uint16_t portnum, uint16_t value) { 60 | portnum &= 0x0FFF; 61 | if (portnum == 0x80) { 62 | debug_log(DEBUG_DETAIL, "Diagnostic port out: %04X\r\n", value); 63 | } 64 | if (ports_cbWriteW[portnum] != NULL) { 65 | (*ports_cbWriteW[portnum])(ports_udata[portnum], portnum, value); 66 | return; 67 | } 68 | port_write(cpu, portnum, (uint8_t)value); 69 | port_write(cpu, portnum + 1, (uint8_t)(value >> 8)); 70 | } 71 | 72 | uint8_t port_read(CPU_t* cpu, uint16_t portnum) { 73 | #ifdef DEBUG_PORTS 74 | debug_log(DEBUG_DETAIL, "port_read @ %03X\r\n", portnum); 75 | #endif 76 | portnum &= 0x0FFF; 77 | if (ports_cbReadB[portnum] != NULL) { 78 | return (*ports_cbReadB[portnum])(ports_udata[portnum], portnum); 79 | } 80 | 81 | return 0xFF; 82 | } 83 | 84 | uint16_t port_readw(CPU_t* cpu, uint16_t portnum) { 85 | uint16_t ret; 86 | portnum &= 0x0FFF; 87 | if (ports_cbReadW[portnum] != NULL) { 88 | return (*ports_cbReadW[portnum])(ports_udata[portnum], portnum); 89 | } 90 | ret = port_read(cpu, portnum); 91 | ret |= (uint16_t)port_read(cpu, portnum + 1) << 8; 92 | return ret; 93 | } 94 | 95 | void ports_cbRegister(uint32_t start, uint32_t count, uint8_t (*readb)(void*, uint32_t), uint16_t (*readw)(void*, uint32_t), void (*writeb)(void*, uint32_t, uint8_t), void (*writew)(void*, uint32_t, uint16_t), void* udata) { 96 | uint32_t i; 97 | for (i = 0; i < count; i++) { 98 | if ((start + i) >= PORTS_COUNT) { 99 | break; 100 | } 101 | ports_cbReadB[start + i] = readb; 102 | ports_cbReadW[start + i] = readw; 103 | ports_cbWriteB[start + i] = writeb; 104 | ports_cbWriteW[start + i] = writew; 105 | ports_udata[start + i] = udata; 106 | } 107 | } 108 | 109 | void ports_init() { 110 | uint32_t i; 111 | for (i = 0; i < PORTS_COUNT; i++) { 112 | ports_cbReadB[i] = NULL; 113 | ports_cbReadW[i] = NULL; 114 | ports_cbWriteB[i] = NULL; 115 | ports_cbWriteW[i] = NULL; 116 | ports_udata[i] = NULL; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /XTulator/modules/io/pcap-win32.c: -------------------------------------------------------------------------------- 1 | /* 2 | XTulator: A portable, open-source 80186 PC emulator. 3 | Copyright (C)2020 Mike Chambers 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | /* 21 | pcap interface for Win32 22 | */ 23 | 24 | #include "../../config.h" 25 | 26 | #ifdef USE_NE2000 27 | 28 | #include 29 | #include 30 | #include 31 | #ifdef _WIN32 32 | #include 33 | #else 34 | #include 35 | pthread_t pcap_dispatchThreadID; 36 | #endif 37 | #include 38 | #include "../../debuglog.h" 39 | #include "../../utility.h" 40 | #include "ne2000.h" 41 | #include "pcap-win32.h" 42 | 43 | pcap_t* pcap_adhandle; 44 | 45 | NE2000_t* pcap_ne2000 = NULL; 46 | 47 | void pcap_listdevs() { 48 | pcap_if_t* alldevs; 49 | pcap_if_t* d; 50 | int i = 0; 51 | char errbuf[PCAP_ERRBUF_SIZE]; 52 | 53 | /* Retrieve the device list from the local machine */ 54 | #ifdef _WIN32 55 | if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL /* auth is not needed */, &alldevs, errbuf) == -1) 56 | #else 57 | if (pcap_findalldevs(&alldevs, errbuf) == -1) 58 | #endif 59 | { 60 | fprintf(stderr, "Error in pcap_findalldevs_ex: %s\n", errbuf); 61 | exit(1); 62 | } 63 | 64 | /* Print the list */ 65 | for (d = alldevs; d != NULL; d = d->next) 66 | { 67 | printf("%d. %s", ++i, d->name); 68 | if (d->description) 69 | printf(" (%s)\n", d->description); 70 | else 71 | printf(" (No description available)\n"); 72 | } 73 | 74 | if (i == 0) 75 | { 76 | printf("\nNo interfaces found! Make sure pcap is installed.\n"); 77 | return; 78 | } 79 | 80 | /* We don't need any more the device list. Free it */ 81 | pcap_freealldevs(alldevs); 82 | } 83 | 84 | int pcap_init(NE2000_t* ne2000, int dev) { 85 | pcap_if_t* alldevs; 86 | pcap_if_t* d; 87 | int i = 0; 88 | char errbuf[PCAP_ERRBUF_SIZE]; 89 | 90 | #ifdef _WIN32 91 | if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1) { 92 | #else 93 | if (pcap_findalldevs(&alldevs, errbuf) == -1) { 94 | #endif 95 | debug_log(DEBUG_ERROR, "Error in pcap_findalldevs: %s\n", errbuf); 96 | return -1; 97 | } 98 | 99 | for (d = alldevs, i = 0; i < dev - 1; d = d->next, i++); 100 | 101 | debug_log(DEBUG_INFO, "[PCAP-WIN32] Initializing pcap library using device: \"%s\"\r\n", d->description ? d->description : "No description available"); 102 | 103 | #ifdef _WIN32 104 | if ((pcap_adhandle = pcap_open(d->name, 65536, PCAP_OPENFLAG_PROMISCUOUS, 1000, NULL, errbuf)) == NULL) { 105 | #else 106 | if ((pcap_adhandle = pcap_open_live(d->name, 65535, 1, -1, NULL)) == NULL) { 107 | #endif 108 | debug_log(DEBUG_ERROR, "\nUnable to open the adapter. %s is not supported by pcap\n", d->name); 109 | pcap_freealldevs(alldevs); 110 | return -1; 111 | } 112 | 113 | pcap_freealldevs(alldevs); 114 | 115 | pcap_ne2000 = ne2000; 116 | 117 | 118 | 119 | #ifdef _WIN32 120 | _beginthread((void*)pcap_dispatchThread, 0, NULL); 121 | #else 122 | pthread_create(&pcap_dispatchThreadID, NULL, pcap_dispatchThread, NULL); 123 | #endif 124 | 125 | return 0; 126 | } 127 | 128 | void pcap_dispatchThread() { 129 | pcap_loop(pcap_adhandle, 0, pcap_rx_handler, NULL); 130 | /*while (running) { 131 | pcap_dispatch(pcap_adhandle, 1, pcap_rx_handler, NULL); 132 | if (pcap_havePacket == 0) { 133 | utility_sleep(1); 134 | } 135 | }*/ 136 | } 137 | 138 | void pcap_rx_handler(u_char* param, const struct pcap_pkthdr* header, const u_char* pkt_data) { 139 | (void)(param); //unused variable 140 | 141 | ne2000_rx_frame(pcap_ne2000, (void*)pkt_data, header->caplen); 142 | } 143 | 144 | void pcap_txPacket(u_char* data, int len) { 145 | pcap_sendpacket(pcap_adhandle, data, len); 146 | } 147 | 148 | #endif 149 | -------------------------------------------------------------------------------- /XTulator/chipset/i8259.c: -------------------------------------------------------------------------------- 1 | /* 2 | XTulator: A portable, open-source 80186 PC emulator. 3 | Copyright (C)2020 Mike Chambers 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | /* 21 | Intel 8259 interrupt controller 22 | */ 23 | 24 | #include 25 | #include 26 | #include "../config.h" 27 | #include "../debuglog.h" 28 | #include "i8259.h" 29 | #include "../ports.h" 30 | 31 | uint8_t i8259_read(I8259_t* i8259, uint16_t portnum) { 32 | #ifdef DEBUG_PIC 33 | debug_log(DEBUG_DETAIL, "[I8259] Read port 0x%X\n", portnum); 34 | #endif 35 | switch (portnum & 1) { 36 | case 0: 37 | if (i8259->readmode == 0) { 38 | return i8259->irr; 39 | } 40 | else { 41 | return i8259->isr; 42 | } 43 | case 1: //read mask register 44 | return i8259->imr; 45 | } 46 | return 0; 47 | } 48 | 49 | void i8259_write(I8259_t* i8259, uint16_t portnum, uint8_t value) { 50 | #ifdef DEBUG_PIC 51 | debug_log(DEBUG_DETAIL, "[I8259] Write port 0x%X: %X\n", portnum, value); 52 | #endif 53 | switch (portnum & 1) { 54 | case 0: 55 | if (value & 0x10) { //ICW1 56 | #ifdef DEBUG_PIC 57 | debug_log(DEBUG_DETAIL, "[I8259] ICW1 = %02X\r\n", value); 58 | #endif 59 | i8259->imr = 0x00; 60 | i8259->icw[1] = value; 61 | i8259->icwstep = 2; 62 | i8259->readmode = 0; 63 | } 64 | else if ((value & 0x08) == 0) { //OCW2 65 | #ifdef DEBUG_PIC 66 | debug_log(DEBUG_DETAIL, "[I8259] OCW2 = %02X\r\n", value); 67 | #endif 68 | i8259->ocw[2] = value; 69 | switch (value & 0xE0) { 70 | case 0x60: //specific EOI 71 | i8259->irr &= ~(1 << (value & 0x03)); 72 | i8259->isr &= ~(1 << (value & 0x03)); 73 | break; 74 | case 0x40: //no operation 75 | break; 76 | case 0x20: //non-specific EOI 77 | i8259->irr &= ~i8259->isr; 78 | i8259->isr = 0x00; 79 | break; 80 | default: //other 81 | #ifdef DEBUG_PIC 82 | debug_log(DEBUG_DETAIL, "[I8259] Unhandled EOI type: %u\r\n", value & 0xE0); 83 | #endif 84 | break; 85 | } 86 | } 87 | else { //OCW3 88 | #ifdef DEBUG_PIC 89 | debug_log(DEBUG_DETAIL, "[I8259] OCW3 = %02X\r\n", value); 90 | #endif 91 | i8259->ocw[3] = value; 92 | if (value & 0x02) { 93 | i8259->readmode = value & 1; 94 | } 95 | } 96 | break; 97 | case 1: 98 | #ifdef DEBUG_PIC 99 | debug_log(DEBUG_DETAIL, "[I8259] ICW%u = %02X\r\n", i8259->icwstep, value); 100 | #endif 101 | switch (i8259->icwstep) { 102 | case 2: //ICW2 103 | i8259->icw[2] = value; 104 | i8259->intoffset = value & 0xF8; 105 | if (i8259->icw[1] & 0x02) { 106 | i8259->icwstep = 4; 107 | } 108 | else { 109 | i8259->icwstep = 3; 110 | } 111 | break; 112 | case 3: //ICW3 113 | i8259->icw[3] = value; 114 | if (i8259->icw[1] & 0x01) { 115 | i8259->icwstep = 4; 116 | } 117 | else { 118 | i8259->icwstep = 5; //done with ICWs 119 | } 120 | break; 121 | case 4: //ICW4 122 | i8259->icw[4] = value; 123 | i8259->icwstep = 5; //done with ICWs 124 | break; 125 | case 5: //just set IMR value now 126 | i8259->imr = value; 127 | break; 128 | } 129 | break; 130 | } 131 | } 132 | 133 | uint8_t i8259_nextintr(I8259_t* i8259) { 134 | uint8_t i, tmpirr; 135 | tmpirr = i8259->irr & (~i8259->imr); //AND request register with inverted mask register 136 | for (i = 0; i < 8; i++) 137 | if ((tmpirr >> i) & 1) { 138 | i8259->irr &= ~(1 << i); 139 | i8259->isr |= (1 << i); 140 | return(i8259->icw[2] + i); 141 | } 142 | return 0; 143 | } 144 | 145 | void i8259_doirq(I8259_t* i8259, uint8_t irqnum) { 146 | #ifdef DEBUG_PIC 147 | debug_log(DEBUG_DETAIL, "[I8259] IRQ %u raised\r\n", irqnum); 148 | #endif 149 | i8259->irr |= (1 << irqnum) & (~i8259->imr); 150 | } 151 | 152 | void i8259_init(I8259_t* i8259) { 153 | memset(i8259, 0, sizeof(I8259_t)); 154 | i8259->intoffset = 8; 155 | ports_cbRegister(0x20, 2, (void*)i8259_read, NULL, (void*)i8259_write, NULL, i8259); 156 | } 157 | -------------------------------------------------------------------------------- /XTulator/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | XTulator: A portable, open-source 80186 PC emulator. 3 | Copyright (C)2020 Mike Chambers 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include "config.h" 24 | #include "args.h" 25 | #include "timing.h" 26 | #include "memory.h" 27 | #include "ports.h" 28 | #include "machine.h" 29 | #include "menus.h" 30 | #include "utility.h" 31 | #include "debuglog.h" 32 | #include "cpu/cpu.h" 33 | #include "chipset/i8259.h" 34 | #include "modules/disk/biosdisk.h" 35 | #include "modules/video/sdlconsole.h" 36 | #include "modules/audio/sdlaudio.h" 37 | #ifdef USE_NE2000 38 | #include "modules/io/pcap-win32.h" 39 | #endif 40 | 41 | char* usemachine = "generic_xt"; //default 42 | 43 | char title[64]; //assuming 64 isn't safe if somebody starts messing with STR_TITLE and STR_VERSION 44 | 45 | uint64_t ops = 0; 46 | uint32_t baudrate = 115200, ramsize = 640, instructionsperloop = 100, cpuLimitTimer; 47 | uint8_t videocard = 0xFF, showMIPS = 0; 48 | volatile uint8_t goCPU = 1, limitCPU = 0; 49 | volatile double speed = 0; 50 | 51 | volatile uint8_t running = 1; 52 | 53 | MACHINE_t machine; 54 | 55 | void optimer(void* dummy) { 56 | ops /= 10000; 57 | if (showMIPS) { 58 | debug_log(DEBUG_INFO, "%llu.%llu MIPS \r", ops / 10, ops % 10); 59 | } 60 | ops = 0; 61 | } 62 | 63 | void cputimer(void* dummy) { 64 | goCPU = 1; 65 | } 66 | 67 | void setspeed(double mhz) { 68 | if (mhz > 0) { 69 | speed = mhz; 70 | instructionsperloop = (uint32_t)((speed * 1000000.0) / 140000.0); 71 | limitCPU = 1; 72 | debug_log(DEBUG_INFO, "[MACHINE] Throttling speed to approximately a %.02f MHz 8088 (%lu instructions/sec)\r\n", speed, instructionsperloop * 10000); 73 | timing_timerEnable(cpuLimitTimer); 74 | } 75 | else { 76 | speed = 0; 77 | instructionsperloop = 100; 78 | limitCPU = 0; 79 | timing_timerDisable(cpuLimitTimer); 80 | } 81 | } 82 | 83 | int main(int argc, char *argv[]) { 84 | 85 | sprintf(title, "%s v%s pre alpha", STR_TITLE, STR_VERSION); 86 | 87 | printf("%s (c)2020 Mike Chambers\r\n", title); 88 | printf("[A portable, open source 80186 PC emulator]\r\n\r\n"); 89 | 90 | ports_init(); 91 | timing_init(); 92 | memory_init(); 93 | #ifdef _WIN32 94 | menus_setMachine(&machine); 95 | #endif 96 | 97 | machine.pcap_if = -1; 98 | if (args_parse(&machine, argc, argv)) { 99 | return -1; 100 | } 101 | 102 | if (sdlconsole_init(title)) { 103 | debug_log(DEBUG_ERROR, "[ERROR] SDL initialization failure\r\n"); 104 | return -1; 105 | } 106 | 107 | if (sdlaudio_init(&machine)) { 108 | debug_log(DEBUG_INFO, "[WARNING] SDL audio initialization failure\r\n"); 109 | } 110 | 111 | if (machine_init(&machine, usemachine) < 0) { 112 | debug_log(DEBUG_ERROR, "[ERROR] Machine initialization failure\r\n"); 113 | return -1; 114 | } 115 | 116 | if (bootdrive == 0xFF) { 117 | if (biosdisk[2].inserted) { 118 | bootdrive = 0x80; 119 | } 120 | else { 121 | bootdrive = 0x00; 122 | } 123 | } 124 | 125 | timing_addTimer(optimer, NULL, 10, TIMING_ENABLED); 126 | cpuLimitTimer = timing_addTimer(cputimer, NULL, 10000, TIMING_DISABLED); 127 | if (speed > 0) { 128 | setspeed(speed); 129 | } 130 | while (running) { 131 | static uint32_t curloop = 0; 132 | if (limitCPU == 0) { 133 | goCPU = 1; 134 | } 135 | if (goCPU) { 136 | cpu_interruptCheck(&machine.CPU, &machine.i8259); 137 | cpu_exec(&machine.CPU, instructionsperloop); 138 | ops += instructionsperloop; 139 | goCPU = 0; 140 | } 141 | timing_loop(); 142 | sdlaudio_updateSampleTiming(); 143 | if (++curloop == 100) { 144 | switch (sdlconsole_loop()) { 145 | case SDLCONSOLE_EVENT_KEY: 146 | machine.KeyState.scancode = sdlconsole_getScancode(); 147 | machine.KeyState.isNew = 1; 148 | i8259_doirq(&machine.i8259, 1); 149 | break; 150 | case SDLCONSOLE_EVENT_QUIT: 151 | running = 0; 152 | break; 153 | case SDLCONSOLE_EVENT_DEBUG_1: 154 | break; 155 | case SDLCONSOLE_EVENT_DEBUG_2: 156 | break; 157 | } 158 | 159 | 160 | curloop = 0; 161 | } 162 | } 163 | 164 | return 0; 165 | } 166 | -------------------------------------------------------------------------------- /XTulator/modules/audio/nukedopl.h: -------------------------------------------------------------------------------- 1 | /* Nuked OPL3 2 | * Copyright (C) 2013-2020 Nuke.YKT 3 | * 4 | * This file is part of Nuked OPL3. 5 | * 6 | * Nuked OPL3 is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU Lesser General Public License as 8 | * published by the Free Software Foundation, either version 2.1 9 | * of the License, or (at your option) any later version. 10 | * 11 | * Nuked OPL3 is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public License 17 | * along with Nuked OPL3. If not, see . 18 | 19 | * Nuked OPL3 emulator. 20 | * Thanks: 21 | * MAME Development Team(Jarek Burczynski, Tatsuyuki Satoh): 22 | * Feedback and Rhythm part calculation information. 23 | * forums.submarine.org.uk(carbon14, opl3): 24 | * Tremolo and phase generator calculation information. 25 | * OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): 26 | * OPL2 ROMs. 27 | * siliconpr0n.org(John McMaster, digshadow): 28 | * YMF262 and VRC VII decaps and die shots. 29 | * 30 | * version: 1.8 31 | */ 32 | 33 | #ifndef OPL_OPL3_H 34 | #define OPL_OPL3_H 35 | 36 | #include 37 | #include "../../config.h" 38 | 39 | #define OPL_WRITEBUF_SIZE 1024 40 | #define OPL_WRITEBUF_DELAY 2 41 | 42 | typedef uintptr_t Bitu; 43 | typedef intptr_t Bits; 44 | typedef uint64_t Bit64u; 45 | typedef int64_t Bit64s; 46 | typedef uint32_t Bit32u; 47 | typedef int32_t Bit32s; 48 | typedef uint16_t Bit16u; 49 | typedef int16_t Bit16s; 50 | typedef uint8_t Bit8u; 51 | typedef int8_t Bit8s; 52 | 53 | typedef struct _opl3_slot opl3_slot; 54 | typedef struct _opl3_channel opl3_channel; 55 | typedef struct _opl3_chip opl3_chip; 56 | 57 | struct _opl3_slot { 58 | opl3_channel* channel; 59 | opl3_chip* chip; 60 | Bit16s out; 61 | Bit16s fbmod; 62 | Bit16s* mod; 63 | Bit16s prout; 64 | Bit16s eg_rout; 65 | Bit16s eg_out; 66 | Bit8u eg_inc; 67 | Bit8u eg_gen; 68 | Bit8u eg_rate; 69 | Bit8u eg_ksl; 70 | Bit8u* trem; 71 | Bit8u reg_vib; 72 | Bit8u reg_type; 73 | Bit8u reg_ksr; 74 | Bit8u reg_mult; 75 | Bit8u reg_ksl; 76 | Bit8u reg_tl; 77 | Bit8u reg_ar; 78 | Bit8u reg_dr; 79 | Bit8u reg_sl; 80 | Bit8u reg_rr; 81 | Bit8u reg_wf; 82 | Bit8u key; 83 | Bit32u pg_reset; 84 | Bit32u pg_phase; 85 | Bit16u pg_phase_out; 86 | Bit8u slot_num; 87 | }; 88 | 89 | struct _opl3_channel { 90 | opl3_slot* slots[2]; 91 | opl3_channel* pair; 92 | opl3_chip* chip; 93 | Bit16s* out[4]; 94 | Bit8u chtype; 95 | Bit16u f_num; 96 | Bit8u block; 97 | Bit8u fb; 98 | Bit8u con; 99 | Bit8u alg; 100 | Bit8u ksv; 101 | Bit16u cha, chb; 102 | Bit8u ch_num; 103 | }; 104 | 105 | typedef struct _opl3_writebuf { 106 | Bit64u time; 107 | Bit16u reg; 108 | Bit8u data; 109 | } opl3_writebuf; 110 | 111 | struct _opl3_chip { 112 | opl3_channel channel[18]; 113 | opl3_slot slot[36]; 114 | Bit16u timer; 115 | Bit64u eg_timer; 116 | Bit8u eg_timerrem; 117 | Bit8u eg_state; 118 | Bit8u eg_add; 119 | Bit8u newm; 120 | Bit8u nts; 121 | Bit8u rhy; 122 | Bit8u vibpos; 123 | Bit8u vibshift; 124 | Bit8u tremolo; 125 | Bit8u tremolopos; 126 | Bit8u tremoloshift; 127 | Bit32u noise; 128 | Bit16s zeromod; 129 | Bit32s mixbuff[2]; 130 | Bit8u rm_hh_bit2; 131 | Bit8u rm_hh_bit3; 132 | Bit8u rm_hh_bit7; 133 | Bit8u rm_hh_bit8; 134 | Bit8u rm_tc_bit3; 135 | Bit8u rm_tc_bit5; 136 | //OPL3L 137 | Bit32s rateratio; 138 | Bit32s samplecnt; 139 | Bit16s oldsamples[2]; 140 | Bit16s samples[2]; 141 | 142 | Bit64u writebuf_samplecnt; 143 | Bit32u writebuf_cur; 144 | Bit32u writebuf_last; 145 | Bit64u writebuf_lasttime; 146 | opl3_writebuf writebuf[OPL_WRITEBUF_SIZE]; 147 | 148 | Bit8u data4; 149 | }; 150 | 151 | void OPL3_Generate(opl3_chip* chip, Bit16s* buf); 152 | void OPL3_GenerateResampled(opl3_chip* chip, Bit16s* buf); 153 | void OPL3_Reset(opl3_chip* chip, Bit32u samplerate); 154 | void OPL3_WriteReg(opl3_chip* chip, Bit16u reg, Bit8u v); 155 | void OPL3_WriteRegBuffered(opl3_chip* chip, Bit16u reg, Bit8u v); 156 | void OPL3_GenerateStream(opl3_chip* chip, Bit16s* sndptr, Bit32u numsamples); 157 | 158 | //added for interfacing with XTulator 159 | int16_t OPL3_getSample(opl3_chip* chip); 160 | void OPL3_write(opl3_chip* chip, uint32_t portnum, uint8_t value); 161 | void OPL3_init(opl3_chip* chip); 162 | 163 | #endif 164 | -------------------------------------------------------------------------------- /XTulator/chipset/uart.c: -------------------------------------------------------------------------------- 1 | /* 2 | XTulator: A portable, open-source 80186 PC emulator. 3 | Copyright (C)2020 Mike Chambers 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | /* 21 | Emulates the 8250 UART. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include "../config.h" 28 | #include "../debuglog.h" 29 | #include "i8259.h" 30 | #include "../ports.h" 31 | #include "uart.h" 32 | 33 | const uint8_t uart_wordmask[4] = { 0x1F, 0x3F, 0x7F, 0xFF }; //5, 6, 7, or 8 bit words based on bits 1-0 in LCR 34 | 35 | void uart_writeport(UART_t* uart, uint16_t addr, uint8_t value) { 36 | #ifdef DEBUG_UART 37 | debug_log(DEBUG_DETAIL, "[UART] Write %03X: %u\r\n", addr, value); 38 | #endif 39 | 40 | addr &= 0x07; 41 | 42 | switch (addr) { 43 | case 0x00: 44 | if (uart->dlab == 0) { 45 | uart->tx = value & uart_wordmask[uart->lcr & 0x03]; 46 | if (uart->mcr & 0x10) { //loopback mode 47 | uart_rxdata(uart, uart->tx); 48 | } else { 49 | if (uart->txCb != NULL) { 50 | (*uart->txCb)(uart->udata, uart->tx); 51 | if (uart->ien & UART_IRQ_TX_ENABLE) { 52 | uart->pendirq |= UART_PENDING_TX; 53 | i8259_doirq(uart->i8259, uart->irq); 54 | } 55 | if (uart->ien & UART_IRQ_LSR_ENABLE) { 56 | uart->pendirq |= UART_PENDING_LSR; 57 | i8259_doirq(uart->i8259, uart->irq); 58 | } 59 | } 60 | } 61 | } else { 62 | uart->divisor = (uart->divisor & 0xFF00) | value; 63 | } 64 | break; 65 | case 0x01: //IEN 66 | if (uart->dlab == 0) { 67 | uart->ien = value; 68 | } else { 69 | uart->divisor = (uart->divisor & 0x00FF) | ((uint16_t)value << 8); 70 | } 71 | break; 72 | case 0x03: //LCR 73 | uart->lcr = value; 74 | uart->dlab = value >> 7; 75 | break; 76 | case 0x04: //MCR 77 | uart->mcr = value; 78 | if (uart->mcrCb != NULL) { 79 | (*uart->mcrCb)(uart->udata2, value); 80 | } 81 | break; 82 | case 0x07: 83 | uart->scratch = value; 84 | break; 85 | } 86 | } 87 | 88 | uint8_t uart_readport(UART_t* uart, uint16_t addr) { 89 | uint8_t ret = 0; // xFF; 90 | 91 | #ifdef DEBUG_UART 92 | debug_log(DEBUG_DETAIL, "[UART] Read %03X\r\n", addr); 93 | #endif 94 | addr &= 0x07; 95 | 96 | switch (addr) { 97 | case 0x00: 98 | if (uart->dlab == 0) { 99 | ret = uart->rx; 100 | uart->rxnew = 0; 101 | uart->pendirq &= ~UART_PENDING_RX; 102 | if (uart->ien & UART_IRQ_LSR_ENABLE) { 103 | uart->pendirq |= UART_PENDING_LSR; 104 | i8259_doirq(uart->i8259, uart->irq); 105 | } 106 | } else { 107 | ret = (uint8_t)uart->divisor; 108 | } 109 | break; 110 | case 0x01: //IEN 111 | if (uart->dlab == 0) { 112 | ret = uart->ien; 113 | } else { 114 | ret = (uint8_t)(uart->divisor >> 8); 115 | } 116 | break; 117 | case 0x02: //IIR 118 | ret = uart->pendirq ? 0x00 : 0x01; 119 | if (uart->pendirq & UART_PENDING_LSR) { 120 | ret |= 0x06; 121 | } 122 | else if (uart->pendirq & UART_PENDING_RX) { 123 | ret |= 0x04; 124 | } 125 | else if (uart->pendirq & UART_PENDING_TX) { 126 | ret |= 0x02; 127 | uart->pendirq &= ~UART_PENDING_TX; 128 | } 129 | else if (uart->pendirq & UART_PENDING_MSR) { 130 | //nothing to do 131 | } 132 | if (uart->pendirq) { 133 | i8259_doirq(uart->i8259, uart->irq); 134 | } 135 | break; 136 | case 0x03: //LCR 137 | ret = uart->lcr; 138 | break; 139 | case 0x04: //MCR 140 | ret = uart->mcr; 141 | break; 142 | case 0x05: //LSR 143 | ret = 0x60; //transmit register always report empty in emulator 144 | ret |= uart->rxnew ? 0x01 : 0x00; 145 | uart->pendirq &= ~UART_PENDING_LSR; 146 | break; 147 | case 0x06: //MSR 148 | ret = uart->msr & 0xF0; 149 | //calculate deltas: 150 | ret |= ((uart->msr & 0x80) != (uart->lastmsr & 0x80)) ? 0x08 : 0x00; 151 | ret |= ((uart->msr & 0x20) != (uart->lastmsr & 0x20)) ? 0x02 : 0x00; 152 | ret |= ((uart->msr & 0x10) != (uart->lastmsr & 0x10)) ? 0x01 : 0x00; 153 | uart->lastmsr = uart->msr; 154 | uart->pendirq &= ~UART_PENDING_MSR; 155 | break; 156 | case 0x07: 157 | ret = 0xFF; // uart->scratch; 158 | break; 159 | } 160 | 161 | return ret; 162 | } 163 | 164 | void uart_rxdata(UART_t* uart, uint8_t value) { 165 | uart->rx = value; 166 | uart->rxnew = 1; 167 | if (uart->ien & UART_IRQ_RX_ENABLE) { 168 | uart->pendirq |= UART_PENDING_RX; 169 | i8259_doirq(uart->i8259, uart->irq); 170 | } 171 | } 172 | 173 | void uart_init(UART_t* uart, I8259_t* i8259, uint16_t base, uint8_t irq, void (*tx)(void*, uint8_t), void* udata, void (*mcr)(void*, uint8_t), void* udata2) { 174 | debug_log(DEBUG_INFO, "[UART] Initializing 8250 UART at base port 0x%03X, IRQ %u\r\n", base, irq); 175 | memset(uart, 0, sizeof(UART_t)); 176 | uart->i8259 = i8259; 177 | uart->irq = irq; 178 | uart->udata = udata; 179 | uart->txCb = tx; 180 | uart->udata2 = udata2; 181 | uart->mcrCb = mcr; 182 | uart->msr = 0x30; 183 | ports_cbRegister(base, 8, (void*)uart_readport, NULL, (void*)uart_writeport, NULL, uart); 184 | } 185 | -------------------------------------------------------------------------------- /XTulator/timing.c: -------------------------------------------------------------------------------- 1 | /* 2 | XTulator: A portable, open-source 80186 PC emulator. 3 | Copyright (C)2020 Mike Chambers 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | #ifdef _WIN32 21 | #include 22 | #else 23 | #include 24 | #endif 25 | #include 26 | #include 27 | #include 28 | #include "config.h" 29 | #include "timing.h" 30 | #include "debuglog.h" 31 | 32 | uint64_t timing_cur; 33 | uint64_t timing_freq; 34 | TIMER* timers = NULL; 35 | uint32_t timers_count = 0; 36 | 37 | int timing_init() { 38 | #ifdef _WIN32 39 | LARGE_INTEGER freq; 40 | //TODO: error handling 41 | QueryPerformanceFrequency(&freq); 42 | timing_freq = (uint64_t)freq.QuadPart; 43 | #else 44 | timing_freq = 1000000; 45 | #endif 46 | return 0; 47 | } 48 | 49 | void timing_loop() { 50 | uint32_t i; 51 | #ifdef _WIN32 52 | LARGE_INTEGER cur; 53 | //TODO: error handling 54 | QueryPerformanceCounter(&cur); 55 | timing_cur = (uint64_t)cur.QuadPart; 56 | #else 57 | struct timeval tv; 58 | gettimeofday(&tv, NULL); 59 | timing_cur = (uint64_t)tv.tv_sec * 1000000 + (uint64_t)tv.tv_usec; 60 | #endif 61 | for (i = 0; i < timers_count; i++) { 62 | if (timing_cur >= (timers[i].previous + timers[i].interval)) { 63 | if (timers[i].enabled != TIMING_DISABLED) { 64 | if (timers[i].callback != NULL) { 65 | (*timers[i].callback)(timers[i].data); 66 | } 67 | timers[i].previous += timers[i].interval; 68 | if ((timing_cur - timers[i].previous) >= (timers[i].interval * 100)) { 69 | timers[i].previous = timing_cur; 70 | } 71 | } 72 | } 73 | } 74 | } 75 | 76 | //Just some code for performance testing 77 | void timing_speedTest() { 78 | #ifdef _WIN32 79 | uint64_t start, i; 80 | LARGE_INTEGER cur; 81 | //TODO: error handling 82 | QueryPerformanceCounter(&cur); 83 | start = (uint64_t)cur.QuadPart; 84 | 85 | i = 0; 86 | while (1) { 87 | QueryPerformanceCounter(&cur); 88 | timing_cur = (uint64_t)cur.QuadPart; 89 | i++; 90 | if ((timing_cur - start) >= timing_freq) break; 91 | } 92 | printf("%llu calls to QPC in 1 second\r\n", i); 93 | #endif 94 | } 95 | 96 | uint32_t timing_addTimerUsingInterval(void* callback, void* data, uint64_t interval, uint8_t enabled) { 97 | TIMER* temp; 98 | uint32_t ret; 99 | #ifdef _WIN32 100 | LARGE_INTEGER cur; 101 | 102 | //TODO: error handling 103 | QueryPerformanceCounter(&cur); 104 | timing_cur = (uint64_t)cur.QuadPart; 105 | #else 106 | struct timeval tv; 107 | gettimeofday(&tv, NULL); 108 | timing_cur = (uint64_t)tv.tv_sec * 1000000 + (uint64_t)tv.tv_usec; 109 | #endif 110 | temp = (TIMER*)realloc(timers, (size_t)sizeof(TIMER) * (timers_count + 1)); 111 | if (temp == NULL) { 112 | //TODO: error handling 113 | return TIMING_ERROR; //NULL; 114 | } 115 | timers = temp; 116 | 117 | timers[timers_count].previous = timing_cur; 118 | timers[timers_count].interval = interval; 119 | timers[timers_count].callback = callback; 120 | timers[timers_count].data = data; 121 | timers[timers_count].enabled = enabled; 122 | 123 | ret = timers_count; 124 | timers_count++; 125 | 126 | return ret; 127 | } 128 | 129 | uint32_t timing_addTimer(void* callback, void* data, double frequency, uint8_t enabled) { 130 | return timing_addTimerUsingInterval(callback, data, (uint64_t)((double)timing_freq / frequency), enabled); 131 | } 132 | 133 | void timing_updateInterval(uint32_t tnum, uint64_t interval) { 134 | if (tnum >= timers_count) { 135 | debug_log(DEBUG_ERROR, "[ERROR] timing_updateInterval() asked to operate on invalid timer\r\n"); 136 | return; 137 | } 138 | timers[tnum].interval = interval; 139 | } 140 | 141 | void timing_updateIntervalFreq(uint32_t tnum, double frequency) { 142 | if (tnum >= timers_count) { 143 | debug_log(DEBUG_ERROR, "[ERROR] timing_updateIntervalFreq() asked to operate on invalid timer\r\n"); 144 | return; 145 | } 146 | timers[tnum].interval = (uint64_t)((double)timing_freq / frequency); 147 | } 148 | 149 | void timing_timerEnable(uint32_t tnum) { 150 | if (tnum >= timers_count) { 151 | debug_log(DEBUG_ERROR, "[ERROR] timing_timerEnable() asked to operate on invalid timer\r\n"); 152 | return; 153 | } 154 | timers[tnum].enabled = TIMING_ENABLED; 155 | timers[tnum].previous = timing_getCur(); 156 | } 157 | 158 | void timing_timerDisable(uint32_t tnum) { 159 | if (tnum >= timers_count) { 160 | debug_log(DEBUG_ERROR, "[ERROR] timing_timerDisable() asked to operate on invalid timer\r\n"); 161 | return; 162 | } 163 | timers[tnum].enabled = TIMING_DISABLED; 164 | } 165 | 166 | uint64_t timing_getFreq() { 167 | return timing_freq; 168 | } 169 | 170 | uint64_t timing_getCur() { 171 | #ifdef _WIN32 172 | LARGE_INTEGER cur; 173 | 174 | //TODO: error handling 175 | QueryPerformanceCounter(&cur); 176 | timing_cur = (uint64_t)cur.QuadPart; 177 | #else 178 | struct timeval tv; 179 | gettimeofday(&tv, NULL); 180 | timing_cur = (uint64_t)tv.tv_sec * 1000000 + (uint64_t)tv.tv_usec; 181 | #endif 182 | 183 | return timing_cur; 184 | } 185 | -------------------------------------------------------------------------------- /XTulator/cpu/cpu.h: -------------------------------------------------------------------------------- 1 | /* 2 | XTulator: A portable, open-source 80186 PC emulator. 3 | Copyright (C)2020 Mike Chambers 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | #ifndef _CPU_H_ 21 | #define _CPU_H_ 22 | 23 | #include 24 | #include "cpuconf.h" 25 | #include "../chipset/i8259.h" 26 | 27 | union _bytewordregs_ { 28 | uint16_t wordregs[8]; 29 | uint8_t byteregs[8]; 30 | }; 31 | 32 | typedef struct { 33 | union _bytewordregs_ regs; 34 | uint8_t opcode, segoverride, reptype, hltstate; 35 | uint16_t segregs[4], savecs, saveip, ip, useseg, oldsp; 36 | uint8_t tempcf, oldcf, cf, pf, af, zf, sf, tf, ifl, df, of, mode, reg, rm; 37 | uint16_t oper1, oper2, res16, disp16, temp16, dummy, stacksize, frametemp; 38 | uint8_t oper1b, oper2b, res8, disp8, temp8, nestlev, addrbyte; 39 | uint32_t temp1, temp2, temp3, temp4, temp5, temp32, tempaddr32, ea; 40 | int32_t result; 41 | uint16_t trap_toggle; 42 | uint64_t totalexec; 43 | void (*int_callback[256])(void*, uint8_t); //Want to pass a CPU object in first param, but it's not defined at this point so use a void* 44 | } CPU_t; 45 | 46 | #define regax 0 47 | #define regcx 1 48 | #define regdx 2 49 | #define regbx 3 50 | #define regsp 4 51 | #define regbp 5 52 | #define regsi 6 53 | #define regdi 7 54 | #define reges 0 55 | #define regcs 1 56 | #define regss 2 57 | #define regds 3 58 | 59 | #ifdef __BIG_ENDIAN__ 60 | #define regal 1 61 | #define regah 0 62 | #define regcl 3 63 | #define regch 2 64 | #define regdl 5 65 | #define regdh 4 66 | #define regbl 7 67 | #define regbh 6 68 | #else 69 | #define regal 0 70 | #define regah 1 71 | #define regcl 2 72 | #define regch 3 73 | #define regdl 4 74 | #define regdh 5 75 | #define regbl 6 76 | #define regbh 7 77 | #endif 78 | 79 | #define StepIP(mycpu, x) mycpu->ip += x 80 | #define getmem8(mycpu, x, y) cpu_read(mycpu, segbase(x) + y) 81 | #define getmem16(mycpu, x, y) cpu_readw(mycpu, segbase(x) + y) 82 | #define putmem8(mycpu, x, y, z) cpu_write(mycpu, segbase(x) + y, z) 83 | #define putmem16(mycpu, x, y, z) cpu_writew(mycpu, segbase(x) + y, z) 84 | #define signext(value) (int16_t)(int8_t)(value) 85 | #define signext32(value) (int32_t)(int16_t)(value) 86 | #define getreg16(mycpu, regid) mycpu->regs.wordregs[regid] 87 | #define getreg8(mycpu, regid) mycpu->regs.byteregs[byteregtable[regid]] 88 | #define putreg16(mycpu, regid, writeval) mycpu->regs.wordregs[regid] = writeval 89 | #define putreg8(mycpu, regid, writeval) mycpu->regs.byteregs[byteregtable[regid]] = writeval 90 | #define getsegreg(mycpu, regid) mycpu->segregs[regid] 91 | #define putsegreg(mycpu, regid, writeval) mycpu->segregs[regid] = writeval 92 | #define segbase(x) ((uint32_t) x << 4) 93 | 94 | #define makeflagsword(x) \ 95 | ( \ 96 | 2 | (uint16_t) x->cf | ((uint16_t) x->pf << 2) | ((uint16_t) x->af << 4) | ((uint16_t) x->zf << 6) | ((uint16_t) x->sf << 7) | \ 97 | ((uint16_t) x->tf << 8) | ((uint16_t) x->ifl << 9) | ((uint16_t) x->df << 10) | ((uint16_t) x->of << 11) \ 98 | ) 99 | 100 | #define decodeflagsword(x,y) { \ 101 | uint16_t tmp; \ 102 | tmp = y; \ 103 | x->cf = tmp & 1; \ 104 | x->pf = (tmp >> 2) & 1; \ 105 | x->af = (tmp >> 4) & 1; \ 106 | x->zf = (tmp >> 6) & 1; \ 107 | x->sf = (tmp >> 7) & 1; \ 108 | x->tf = (tmp >> 8) & 1; \ 109 | x->ifl = (tmp >> 9) & 1; \ 110 | x->df = (tmp >> 10) & 1; \ 111 | x->of = (tmp >> 11) & 1; \ 112 | } 113 | 114 | #define modregrm(x) { \ 115 | x->addrbyte = getmem8(x, x->segregs[regcs], x->ip); \ 116 | StepIP(x, 1); \ 117 | x->mode = x->addrbyte >> 6; \ 118 | x->reg = (x->addrbyte >> 3) & 7; \ 119 | x->rm = x->addrbyte & 7; \ 120 | switch(x->mode) \ 121 | { \ 122 | case 0: \ 123 | if(x->rm == 6) { \ 124 | x->disp16 = getmem16(x, x->segregs[regcs], x->ip); \ 125 | StepIP(x, 2); \ 126 | } \ 127 | if(((x->rm == 2) || (x->rm == 3)) && !x->segoverride) { \ 128 | x->useseg = x->segregs[regss]; \ 129 | } \ 130 | break; \ 131 | \ 132 | case 1: \ 133 | x->disp16 = signext(getmem8(x, x->segregs[regcs], x->ip)); \ 134 | StepIP(x, 1); \ 135 | if(((x->rm == 2) || (x->rm == 3) || (x->rm == 6)) && !x->segoverride) { \ 136 | x->useseg = x->segregs[regss]; \ 137 | } \ 138 | break; \ 139 | \ 140 | case 2: \ 141 | x->disp16 = getmem16(x, x->segregs[regcs], x->ip); \ 142 | StepIP(x, 2); \ 143 | if(((x->rm == 2) || (x->rm == 3) || (x->rm == 6)) && !x->segoverride) { \ 144 | x->useseg = x->segregs[regss]; \ 145 | } \ 146 | break; \ 147 | \ 148 | default: \ 149 | x->disp8 = 0; \ 150 | x->disp16 = 0; \ 151 | } \ 152 | } 153 | 154 | 155 | uint8_t cpu_read(CPU_t* cpu, uint32_t addr); 156 | uint16_t cpu_readw(CPU_t* cpu, uint32_t addr); 157 | void cpu_write(CPU_t* cpu, uint32_t addr32, uint8_t value); 158 | void cpu_writew(CPU_t* cpu, uint32_t addr32, uint16_t value); 159 | void cpu_intcall(CPU_t* cpu, uint8_t intnum); 160 | void cpu_reset(CPU_t* cpu); 161 | void cpu_interruptCheck(CPU_t* cpu, I8259_t* i8259); 162 | void cpu_exec(CPU_t* cpu, uint32_t execloops); 163 | void port_write(CPU_t* cpu, uint16_t portnum, uint8_t value); 164 | void port_writew(CPU_t* cpu, uint16_t portnum, uint16_t value); 165 | uint8_t port_read(CPU_t* cpu, uint16_t portnum); 166 | uint16_t port_readw(CPU_t* cpu, uint16_t portnum); 167 | void cpu_registerIntCallback(CPU_t* cpu, uint8_t interrupt, void (*cb)(CPU_t*, uint8_t)); 168 | 169 | #endif 170 | -------------------------------------------------------------------------------- /XTulator/modules/audio/sdlaudio.c: -------------------------------------------------------------------------------- 1 | /* 2 | XTulator: A portable, open-source 80186 PC emulator. 3 | Copyright (C)2020 Mike Chambers 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | /* 21 | NOTE: Maybe implement some kind of crude time-stretching? 22 | */ 23 | 24 | #include "../../config.h" 25 | #include 26 | #include 27 | #include 28 | #include "sdlaudio.h" 29 | #include "pcspeaker.h" 30 | #include "opl2.h" 31 | #include "blaster.h" 32 | #include "../../machine.h" 33 | #include "../../timing.h" 34 | #include "../../utility.h" 35 | #include "../../debuglog.h" 36 | #ifdef _WIN32 37 | #include 38 | #include 39 | #include 40 | #else 41 | #include 42 | #include 43 | #include 44 | pthread_t sdlaudio_sampleThreadID; 45 | #endif 46 | 47 | SDL_mutex* sdlaudio_mutex = NULL; 48 | SDL_cond* sdlaudio_canFill = NULL; 49 | SDL_cond* sdlaudio_canBuffer = NULL; 50 | 51 | int16_t sdlaudio_buffer[SAMPLE_BUFFER], sdlaudio_bufferpos = 0; 52 | double sdlaudio_rateFast; 53 | 54 | uint8_t sdlaudio_firstfill = 1, sdlaudio_timeIdx = 0; 55 | SDL_AudioSpec sdlaudio_gotspec; 56 | uint32_t sdlaudio_timer; 57 | uint64_t sdlaudio_cbTime[10] = { //history of time between callbacks to dynamically adjust our sample generation 58 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 59 | }; 60 | double sdlaudio_genSampRate = SAMPLE_RATE; 61 | double sdlaudio_genInterval; 62 | 63 | volatile uint8_t sdlaudio_updateTiming = 0; 64 | 65 | MACHINE_t* sdlaudio_useMachine = NULL; 66 | 67 | void sdlaudio_moveBuffer(int16_t* dst, int len); 68 | 69 | void sdlaudio_fill(void* udata, uint8_t* stream, int len) { 70 | static uint8_t timesmissed = 0; 71 | static uint64_t curtime, lasttime; 72 | 73 | //SDL_LockMutex(sdlaudio_mutex); 74 | //SDL_CondWait(sdlaudio_canFill, sdlaudio_mutex); 75 | 76 | sdlaudio_moveBuffer((int16_t*)stream, len); 77 | 78 | //SDL_UnlockMutex(sdlaudio_mutex); 79 | //SDL_CondSignal(sdlaudio_canBuffer); 80 | } 81 | 82 | int sdlaudio_init(MACHINE_t* machine) { 83 | SDL_AudioSpec wanted; 84 | 85 | if (machine == NULL) return -1; 86 | 87 | if (SDL_Init(SDL_INIT_AUDIO)) return -1; 88 | 89 | sdlaudio_mutex = SDL_CreateMutex(); 90 | sdlaudio_canFill = SDL_CreateCond(); 91 | sdlaudio_canBuffer = SDL_CreateCond(); 92 | 93 | wanted.freq = SAMPLE_RATE; 94 | wanted.format = AUDIO_S16; 95 | wanted.channels = 1; 96 | wanted.samples = SAMPLE_BUFFER >> 2; 97 | wanted.callback = sdlaudio_fill; 98 | wanted.userdata = NULL; 99 | 100 | if (SDL_OpenAudio(&wanted, NULL) < 0) { 101 | return -1; 102 | } 103 | 104 | sdlaudio_useMachine = machine; 105 | 106 | sdlaudio_rateFast = (double)(SAMPLE_RATE) * 1.01; 107 | 108 | sdlaudio_timer = timing_addTimer(sdlaudio_generateSample, NULL, SAMPLE_RATE, TIMING_ENABLED); 109 | 110 | SDL_PauseAudio(1); 111 | SDL_CondSignal(sdlaudio_canFill); 112 | SDL_CondSignal(sdlaudio_canBuffer); 113 | 114 | return 0; 115 | } 116 | 117 | void sdlaudio_bufferSample(int16_t val) { 118 | if (sdlaudio_bufferpos == SAMPLE_BUFFER) { //this shouldn't happen 119 | return; 120 | } 121 | 122 | //SDL_LockMutex(sdlaudio_mutex); 123 | //SDL_CondWait(sdlaudio_canBuffer, sdlaudio_mutex); 124 | 125 | sdlaudio_buffer[sdlaudio_bufferpos++] = val; 126 | 127 | if (sdlaudio_bufferpos < (int)((double)(SAMPLE_BUFFER) * 0.5)) { 128 | sdlaudio_updateTiming = SDLAUDIO_TIMING_FAST; 129 | } 130 | else if (sdlaudio_bufferpos >= (int)((double)(SAMPLE_BUFFER) * 0.75)) { 131 | sdlaudio_updateTiming = SDLAUDIO_TIMING_NORMAL; 132 | } 133 | 134 | if (sdlaudio_bufferpos == SAMPLE_BUFFER) { 135 | timing_timerDisable(sdlaudio_timer); 136 | } 137 | 138 | //SDL_UnlockMutex(sdlaudio_mutex); 139 | //SDL_CondSignal(sdlaudio_canFill); 140 | } 141 | 142 | void sdlaudio_updateSampleTiming() { 143 | if (sdlaudio_updateTiming == SDLAUDIO_TIMING_FAST) { 144 | timing_updateIntervalFreq(sdlaudio_timer, sdlaudio_rateFast); 145 | } 146 | else if (sdlaudio_updateTiming == SDLAUDIO_TIMING_NORMAL) { 147 | timing_updateIntervalFreq(sdlaudio_timer, SAMPLE_RATE); 148 | SDL_PauseAudio(0); 149 | } 150 | sdlaudio_updateTiming = 0; 151 | } 152 | 153 | //I need to make this use a ring buffer soon... 154 | void sdlaudio_moveBuffer(int16_t* dst, int len) { 155 | int i; 156 | memset(dst, 0, len); 157 | 158 | if (sdlaudio_bufferpos < (int)((double)(SAMPLE_BUFFER) * 0.75)) { 159 | timing_timerEnable(sdlaudio_timer); 160 | } 161 | 162 | if ((sdlaudio_bufferpos << 1) < len) { 163 | SDL_PauseAudio(1); 164 | return; 165 | } 166 | 167 | for (i = 0; i < (len >> 1); i++) { 168 | dst[i] = sdlaudio_buffer[i]; 169 | } 170 | for (; i < sdlaudio_bufferpos; i++) { 171 | sdlaudio_buffer[i - (len >> 1)] = sdlaudio_buffer[i]; 172 | } 173 | sdlaudio_bufferpos -= len >> 1; 174 | } 175 | 176 | void sdlaudio_generateSample(void* dummy) { 177 | int16_t val; 178 | 179 | val = pcspeaker_getSample(&sdlaudio_useMachine->pcspeaker) / 3; 180 | //val += opl2_generateSample(&sdlaudio_useMachine->OPL2) / 3; 181 | if (sdlaudio_useMachine->mixOPL) { 182 | int16_t OPLsample[2]; 183 | //val += sdlaudio_getOPLsample() / 2; 184 | OPL3_GenerateStream(&sdlaudio_useMachine->OPL3, OPLsample, 1); 185 | val += OPLsample[0] / 2; 186 | } 187 | if (sdlaudio_useMachine->mixBlaster) { 188 | val += blaster_getSample(&sdlaudio_useMachine->blaster) / 3; 189 | } 190 | 191 | sdlaudio_bufferSample(val); 192 | } 193 | -------------------------------------------------------------------------------- /XTulator/chipset/i8253.c: -------------------------------------------------------------------------------- 1 | /* 2 | XTulator: A portable, open-source 80186 PC emulator. 3 | Copyright (C)2020 Mike Chambers 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | /* 21 | Intel 8253 timer 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include "i8253.h" 28 | #include "i8259.h" 29 | #include "../config.h" 30 | #include "../modules/audio/pcspeaker.h" 31 | #include "../timing.h" 32 | #include "../ports.h" 33 | #include "../debuglog.h" 34 | 35 | uint32_t i8253_timers[3]; 36 | 37 | void i8253_write(I8253_t* i8253, uint16_t portnum, uint8_t value) { 38 | uint8_t sel, rl, loaded; 39 | portnum &= 3; 40 | 41 | loaded = 0; 42 | switch (portnum) { 43 | case 0: //load counters 44 | case 1: 45 | case 2: 46 | switch (i8253->rlmode[portnum]) { 47 | case 1: //MSB only 48 | i8253->reload[portnum] = (int32_t)value << 8; 49 | i8253->active[portnum] = 1; 50 | loaded = 1; 51 | break; 52 | case 2: //LSB only 53 | i8253->reload[portnum] = value; 54 | i8253->active[portnum] = 1; 55 | loaded = 1; 56 | break; 57 | case 3: //LSB, then MSB 58 | if (i8253->dataflipflop[portnum] == 0) { //LSB 59 | i8253->reload[portnum] = (i8253->reload[portnum] & 0xFF00) | value; 60 | } else { //MSB 61 | i8253->reload[portnum] = (i8253->reload[portnum] & 0x00FF) | ((int32_t)value << 8); 62 | i8253->counter[portnum] = i8253->reload[portnum]; 63 | if (i8253->reload[portnum] == 0) { 64 | i8253->reload[portnum] = 65536; 65 | } 66 | i8253->active[portnum] = 1; 67 | loaded = 1; 68 | #ifdef DEBUG_PIT 69 | debug_log(DEBUG_DETAIL, "I8253: Counter %u reload = %d\r\n", portnum, i8253->reload[portnum]); 70 | #endif 71 | } 72 | i8253->dataflipflop[portnum] ^= 1; 73 | break; 74 | } 75 | if (loaded) switch (i8253->mode[portnum]) { 76 | case 0: 77 | case 1: 78 | i8253->out[portnum] = 0; 79 | break; 80 | case 2: 81 | case 3: 82 | i8253->out[portnum] = 1; 83 | break; 84 | } 85 | break; 86 | case 3: //control word 87 | sel = value >> 6; 88 | if (sel == 3) { //illegal 89 | return; 90 | } 91 | rl = (value >> 4) & 3; //read/load mode 92 | if (rl == 0) { //counter latching operation 93 | i8253->latch[sel] = i8253->counter[sel]; 94 | } else { //set mode 95 | i8253->rlmode[sel] = rl; 96 | i8253->mode[sel] = (value >> 1) & 7; 97 | if (i8253->mode[sel] & 0x02) { 98 | i8253->mode[sel] &= 3; //MSB is "don't care" if bit 1 is set 99 | } 100 | i8253->bcd[sel] = value & 1; 101 | #ifdef DEBUG_PIT 102 | debug_log(DEBUG_DETAIL, "I8253: Counter %u mode = %u\r\n", sel, i8253->mode[sel]); 103 | #endif 104 | } 105 | i8253->dataflipflop[sel] = 0; 106 | break; 107 | } 108 | } 109 | 110 | uint8_t i8253_read(I8253_t* i8253, uint16_t portnum) { 111 | uint8_t ret; 112 | portnum &= 3; 113 | 114 | if (portnum == 3) { 115 | return 0xFF; //no read of control word possible 116 | } 117 | 118 | switch (i8253->rlmode[portnum]) { 119 | case 1: //MSB only 120 | return i8253->latch[portnum] >> 8; 121 | case 2: //LSB only 122 | return (uint8_t)i8253->latch[portnum]; 123 | default: //LSB, then MSB (case 3, but say default so MSVC stops warning me about control paths not all returning a value) 124 | if (i8253->dataflipflop[portnum] == 0) { //LSB 125 | ret = (uint8_t)i8253->latch[portnum]; 126 | } else { //MSB 127 | ret = i8253->latch[portnum] >> 8; 128 | } 129 | i8253->dataflipflop[portnum] ^= 1; 130 | return ret; 131 | } 132 | } 133 | 134 | void i8253_timerCallback0(I8259_t* i8259) { 135 | i8259_doirq(i8259, 0); 136 | } 137 | 138 | void i8253_timerCallback1(I8259_t* i8259) { 139 | } 140 | 141 | void i8253_timerCallback2(I8259_t* i8259) { 142 | } 143 | 144 | void i8253_tickCallback(I8253CB_t* i8253cb) { 145 | I8253_t* i8253; 146 | I8259_t* i8259; 147 | uint8_t i; 148 | 149 | i8253 = i8253cb->i8253; 150 | i8259 = i8253cb->i8259; 151 | for (i = 0; i < 3; i++) { 152 | if ((i == 2) && (i8253->mode[2] != 3)) pcspeaker_setGateState(i8253->cbdata.pcspeaker, PC_SPEAKER_GATE_TIMER2, 0); 153 | if (i8253->active[i]) switch (i8253->mode[i]) { 154 | case 0: //interrupt on terminal count 155 | i8253->counter[i] -= 25; 156 | if (i8253->counter[i] <= 0) { 157 | i8253->counter[i] = 0; 158 | i8253->out[i] = 1; 159 | if (i == 0) i8253_timerCallback0(i8259); 160 | } 161 | break; 162 | case 2: //rate generator 163 | i8253->counter[i] -= 25; 164 | if (i8253->counter[i] <= 0) { 165 | i8253->out[i] ^= 1; 166 | //if (i8253->out[i] == 0) { 167 | if (i == 0) i8253_timerCallback0(i8259); 168 | //} 169 | i8253->counter[i] += i8253->reload[i]; 170 | } 171 | break; 172 | case 3: //square wave generator 173 | i8253->counter[i] -= 50; 174 | if (i8253->counter[i] <= 0) { 175 | i8253->out[i] ^= 1; 176 | if (i8253->out[i] == 0) { 177 | if (i == 0) i8253_timerCallback0(i8259); 178 | } 179 | if (i == 2) pcspeaker_setGateState(i8253->cbdata.pcspeaker, PC_SPEAKER_GATE_TIMER2, (i8253->reload[i] < 50) ? 0 : i8253->out[i]); 180 | i8253->counter[i] += i8253->reload[i]; 181 | } 182 | break; 183 | default: 184 | #ifdef DEBUG_PIT 185 | debug_log(DEBUG_DETAIL, "I8253: Unknown mode %u on counter %u\r\n", i8253->mode[i], i); 186 | #endif 187 | break; 188 | } 189 | } 190 | } 191 | 192 | void i8253_init(I8253_t* i8253, I8259_t* i8259, PCSPEAKER_t* pcspeaker) { 193 | memset(i8253, 0, sizeof(I8253_t)); 194 | 195 | i8253->cbdata.i8253 = i8253; 196 | i8253->cbdata.i8259 = i8259; 197 | i8253->cbdata.pcspeaker = pcspeaker; 198 | 199 | timing_addTimer(i8253_tickCallback, (void*)(&i8253->cbdata), 48000, TIMING_ENABLED); //79545.47 200 | 201 | ports_cbRegister(0x40, 4, (void*)i8253_read, NULL, (void*)i8253_write, NULL, i8253); 202 | } 203 | -------------------------------------------------------------------------------- /XTulator/modules/io/bswap.h: -------------------------------------------------------------------------------- 1 | #ifndef _BSWAP_H 2 | #define _BSWAP_H 3 | 4 | //#include "config-host.h" 5 | 6 | //#include 7 | #include 8 | 9 | #ifdef HAVE_BYTESWAP_H 10 | #include 11 | #else 12 | 13 | #define bswap_16(x) \ 14 | ({ \ 15 | uint16_t __x = (x); \ 16 | ((uint16_t)( \ 17 | (((uint16_t)(__x) & (uint16_t)0x00ffU) << 8) | \ 18 | (((uint16_t)(__x) & (uint16_t)0xff00U) >> 8) )); \ 19 | }) 20 | 21 | #define bswap_32(x) \ 22 | ({ \ 23 | uint32_t __x = (x); \ 24 | ((uint32_t)( \ 25 | (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \ 26 | (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \ 27 | (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \ 28 | (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )); \ 29 | }) 30 | 31 | #define bswap_64(x) \ 32 | ({ \ 33 | uint64_t __x = (x); \ 34 | ((uint64_t)( \ 35 | (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000000000ffULL) << 56) | \ 36 | (uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000000000ff00ULL) << 40) | \ 37 | (uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000000000ff0000ULL) << 24) | \ 38 | (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000ff000000ULL) << 8) | \ 39 | (uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000ff00000000ULL) >> 8) | \ 40 | (uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000ff0000000000ULL) >> 24) | \ 41 | (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00ff000000000000ULL) >> 40) | \ 42 | (uint64_t)(((uint64_t)(__x) & (uint64_t)0xff00000000000000ULL) >> 56) )); \ 43 | }) 44 | 45 | #endif /* !HAVE_BYTESWAP_H */ 46 | 47 | uint16_t bswap16(uint16_t x) 48 | { 49 | uint16_t __x = (x); \ 50 | return ((uint16_t)(\ 51 | (((uint16_t)(__x) & (uint16_t)0x00ffU) << 8) | \ 52 | (((uint16_t)(__x) & (uint16_t)0xff00U) >> 8))); \ 53 | } 54 | 55 | uint32_t bswap32(uint32_t x) 56 | { 57 | uint32_t __x = (x); \ 58 | return ((uint32_t)(\ 59 | (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \ 60 | (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \ 61 | (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \ 62 | (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24))); \ 63 | } 64 | 65 | uint64_t bswap64(uint64_t x) 66 | { 67 | uint64_t __x = (x); \ 68 | return ((uint64_t)(\ 69 | (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000000000ffULL) << 56) | \ 70 | (uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000000000ff00ULL) << 40) | \ 71 | (uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000000000ff0000ULL) << 24) | \ 72 | (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000ff000000ULL) << 8) | \ 73 | (uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000ff00000000ULL) >> 8) | \ 74 | (uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000ff0000000000ULL) >> 24) | \ 75 | (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00ff000000000000ULL) >> 40) | \ 76 | (uint64_t)(((uint64_t)(__x) & (uint64_t)0xff00000000000000ULL) >> 56))); \ 77 | } 78 | 79 | void bswap16s(uint16_t* s) 80 | { 81 | *s = bswap16(*s); 82 | } 83 | 84 | void bswap32s(uint32_t* s) 85 | { 86 | *s = bswap32(*s); 87 | } 88 | 89 | void bswap64s(uint64_t* s) 90 | { 91 | *s = bswap64(*s); 92 | } 93 | 94 | #if defined(WORDS_BIGENDIAN) 95 | #define be_bswap(v, size) (v) 96 | #define le_bswap(v, size) bswap ## size(v) 97 | #define be_bswaps(v, size) 98 | #define le_bswaps(p, size) *p = bswap ## size(*p); 99 | #else 100 | #define le_bswap(v, size) (v) 101 | #define be_bswap(v, size) bswap ## size(v) 102 | #define le_bswaps(v, size) 103 | #define be_bswaps(p, size) *p = bswap ## size(*p); 104 | #endif 105 | 106 | #define CPU_CONVERT(endian, size, type)\ 107 | type endian ## size ## _to_cpu(type v)\ 108 | {\ 109 | return endian ## _bswap(v, size);\ 110 | }\ 111 | \ 112 | type cpu_to_ ## endian ## size(type v)\ 113 | {\ 114 | return endian ## _bswap(v, size);\ 115 | }\ 116 | \ 117 | void endian ## size ## _to_cpus(type *p)\ 118 | {\ 119 | endian ## _bswaps(p, size)\ 120 | }\ 121 | \ 122 | void cpu_to_ ## endian ## size ## s(type *p)\ 123 | {\ 124 | endian ## _bswaps(p, size)\ 125 | }\ 126 | \ 127 | type endian ## size ## _to_cpup(const type *p)\ 128 | {\ 129 | return endian ## size ## _to_cpu(*p);\ 130 | }\ 131 | \ 132 | void cpu_to_ ## endian ## size ## w(type *p, type v)\ 133 | {\ 134 | *p = cpu_to_ ## endian ## size(v);\ 135 | } 136 | 137 | CPU_CONVERT(be, 16, uint16_t) 138 | CPU_CONVERT(be, 32, uint32_t) 139 | CPU_CONVERT(be, 64, uint64_t) 140 | 141 | CPU_CONVERT(le, 16, uint16_t) 142 | CPU_CONVERT(le, 32, uint32_t) 143 | CPU_CONVERT(le, 64, uint64_t) 144 | 145 | /* unaligned versions (optimized for frequent unaligned accesses)*/ 146 | 147 | #if defined(__i386__) || defined(__powerpc__) 148 | 149 | #define cpu_to_le16wu(p, v) cpu_to_le16w(p, v) 150 | #define cpu_to_le32wu(p, v) cpu_to_le32w(p, v) 151 | #define le16_to_cpupu(p) le16_to_cpup(p) 152 | #define le32_to_cpupu(p) le32_to_cpup(p) 153 | 154 | #define cpu_to_be16wu(p, v) cpu_to_be16w(p, v) 155 | #define cpu_to_be32wu(p, v) cpu_to_be32w(p, v) 156 | 157 | #else 158 | 159 | void cpu_to_le16wu(uint16_t* p, uint16_t v) 160 | { 161 | uint8_t* p1 = (uint8_t*)p; 162 | 163 | p1[0] = (uint8_t)v; 164 | p1[1] = (uint8_t)(v >> 8); 165 | } 166 | 167 | void cpu_to_le32wu(uint32_t* p, uint32_t v) 168 | { 169 | uint8_t* p1 = (uint8_t*)p; 170 | 171 | p1[0] = v; 172 | p1[1] = v >> 8; 173 | p1[2] = v >> 16; 174 | p1[3] = v >> 24; 175 | } 176 | 177 | uint16_t le16_to_cpupu(const uint16_t* p) 178 | { 179 | const uint8_t* p1 = (const uint8_t*)p; 180 | return p1[0] | (p1[1] << 8); 181 | } 182 | 183 | uint32_t le32_to_cpupu(const uint32_t* p) 184 | { 185 | const uint8_t* p1 = (const uint8_t*)p; 186 | return p1[0] | (p1[1] << 8) | (p1[2] << 16) | (p1[3] << 24); 187 | } 188 | 189 | void cpu_to_be16wu(uint16_t* p, uint16_t v) 190 | { 191 | uint8_t* p1 = (uint8_t*)p; 192 | 193 | p1[0] = (uint8_t)(v >> 8); 194 | p1[1] = (uint8_t)v; 195 | } 196 | 197 | void cpu_to_be32wu(uint32_t* p, uint32_t v) 198 | { 199 | uint8_t* p1 = (uint8_t*)p; 200 | 201 | p1[0] = v >> 24; 202 | p1[1] = v >> 16; 203 | p1[2] = v >> 8; 204 | p1[3] = v; 205 | } 206 | 207 | #endif 208 | 209 | #ifdef WORDS_BIGENDIAN 210 | #define cpu_to_32wu cpu_to_be32wu 211 | #else 212 | #define cpu_to_32wu cpu_to_le32wu 213 | #endif 214 | 215 | #undef le_bswap 216 | #undef be_bswap 217 | #undef le_bswaps 218 | #undef be_bswaps 219 | 220 | #endif /* BSWAP_H */ 221 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # XTulator - A portable, open source (GPLv2) 80186 PC emulator 2 | 3 | ### About 4 | 5 | XTulator is an x86 PC emulator that is designed to run software that was written for Intel processors up to the 80186. It's able to run MS-DOS, FreeDOS, Windows 3.0, and many games of the era. It supports graphics up to the EGA/VGA standard, and emulates the Sound Blaster 2.0 as well as Adlib/OPL2 (using Nuked OPL). It also emulates peripherals like the standard Microsoft-compatible serial mouse and a serial modem, which can simulate phone line connections via TCP. An NE2000 Ethernet adapter is also emulated using pcap. 6 | 7 | This is actually a rewrite of an emulator I wrote many years ago. It was poorly implemented, even though it worked fairly well. It had many hacks and a poor architecture, but most old 80186 software could still run under it. I've just never quite been happy with it, so I am writing this new emulator to be more sanely coded, and more accurate. I'm aiming to create a much more modular architecture this time around and avoid the design flaws which turned the old code into a mess. 8 | 9 | ### Re-write goals 10 | 11 | - More sane architecture in general 12 | - Minimize (preferably eliminate) use of shared globals 13 | - Implement a sort of generic "virtual ISA card" interface 14 | - Keep CPU and other modules as independent of each other as possible 15 | - Improve accuracy of chipset and components emulation 16 | - Maintain host platform-independence 17 | - Implement proper hard drive and floppy disk controller interfaces, rather than "cheating" with HLE like before 18 | 19 | ### Current status 20 | 21 | WARNING: This software is still currently in the early stages, and not really ready for general use. If you enjoy testing experimental new software, this is for you! 22 | 23 | It supports multiple machine definitions which are selectable via the command line. (-machine option, use -h for help) Only the "generic_xt" machine is currently bootable. This is currently making use of the [Super PC/Turbo XT BIOS](http://www.phatcode.net/downloads.php?id=101) from [phatcode.net](http://www.phatcode.net) which is attributed to Ya'akov Miles and Jon Petrosky. 24 | 25 | I hope to get the stock IBM XT and other BIOSes bootable in the near future. They don't seem to like something about my chipset implementation, which is my highest priority bug at the moment. 26 | 27 | You cannot change floppy images on the fly yet unless you're using the Windows build. I'm trying to come up with a cross-platform GUI method to do this and change other options in real-time. If you need to install an OS and programs on a hard disk image under Linux/Mac, it may be best for now to do that in something like QEMU or Fake86, and then boot the HDD image in XTulator. 28 | 29 | Checkmarks below mean that feature is implemented enough to boot and run things with the "generic_xt" machine definition. See comments below for details. 30 | 31 | - [x] CPU - Complete 32 | - [x] Implement support for multiple machine defnitions (This exists, but only the generic_xt machine boots, the other BIOSes have issues. This is a high priority thing to fix) 33 | - [x] Intel 8253 timer (Re-implemented, but needs some work to be fully accurate) 34 | - [x] Intel 8259 interrupt controller (Working, also needs some more attention. This may be the cause of some of the BIOS issues.) 35 | - [x] Intel 8237 DMA controller (Partially implemented, same as above regarding BIOS boot issues) 36 | - [x] Intel 8255 PPI controller 37 | - [x] Re-implement proper system timing 38 | - [x] Re-implement proper video rendering 39 | - [x] Rewrite and improve EGA/VGA handling from old emulator (EGA/VGA works now, with some bugs) 40 | - [ ] Implement proper IDE and floppy controller interfaces (Work begun on FDC) 41 | - [x] Keyboard input 42 | - [x] Mouse input 43 | - [x] PC speaker (Works, but need to figure out why RealSound doesn't work in Links) 44 | - [x] Adlib/OPL2 (Using Nuked OPL for now. Still working on my own OPL code, which is making noises, but not good noises.) 45 | - [x] Sound Blaster (SB 2.0 compatibility, but still need to add high speed DMA mode. Glitches with a couple of games I've tried, working on it...) 46 | - [x] RTC (Need to fix this under non-Win32 OSes) 47 | - [x] Emulate a Hayes-compatible a serial modem using TCP (somewhat working, only in Windows) 48 | - [x] Novell NE2000 ethernet (Adapted source from Bochs) 49 | - [ ] Implement Intel 8080 emulation in the NEC V20 mode (low priority) 50 | 51 | ### Pre-compiled Win32 release 52 | 53 | You can download a pre-compiled Win32 binary [by clicking here](https://xtulator.com/downloads/XTulator-0.20.7.15-pre_alpha.zip). It's v0.20.7.15 pre-alpha. It also includes a sample hard disk image with some very old shareware to test with, as well as the public domain BIOS ROM for the generic_xt machine. To use the NE2000 Ethernet emulation, you will need to install [Npcap](https://nmap.org/npcap/). 54 | 55 | ### Compiling from source 56 | 57 | This project has been tested to compile in Visual Studio 2019, Debian 10 and MacOS, though I may occasionally break Linux/MacOS support as it's not my main platform and I don't always test it at this point. Don't be surprised if it crashes and burns. Interpret "committed to master" as "this worked on Windows" for the time being. 58 | 59 | ##### Windows (Visual Studio 2019) 60 | 61 | The repository includes a Visual Studio 2019 solution and project. You will need to install the [SDL2](http://www.libsdl.org) and [Npcap](https://nmap.org/npcap/) dev libraries. 62 | 63 | ##### Linux 64 | 65 | Sorry, there's no configure script or makefile yet, so you'l have to compile and link it by hand. 66 | 67 | You will need the SDL2 and pcap dev libraries. On Debian/Ubuntu and related distributions, you can install it with the following line. 68 | 69 |
sudo apt-get install libsdl2-dev libpcap-dev
70 | 71 | After this, the following line should successfully compile the code. 72 | 73 |
gcc -O3 -o XTulator XTulator/*.c XTulator/chipset/*.c XTulator/cpu/cpu.c XTulator/modules/audio/*.c XTulator/modules/disk/*.c XTulator/modules/input/*.c XTulator/modules/io/*.c XTulator/modules/video/*.c -lm -lpthread `pcap-config --cflags --libs` `sdl2-config --cflags --libs`
74 | 75 | 76 | ### Some screenshots 77 | 78 | ![Screenshot 1](https://i.imgur.com/Qkut2rl.png) 79 | 80 | ![Screenshot 2](https://i.imgur.com/uEgW0WN.png) 81 | 82 | ![Screenshot 3](https://i.imgur.com/JCkGRdO.png) 83 | 84 | ![Screenshot 4](https://i.imgur.com/69z2BwQ.png) 85 | 86 | ![Screenshot 5](https://i.imgur.com/ieLk41s.png) 87 | 88 | ![Screenshot 6](https://i.imgur.com/0CGsd1F.png) 89 | 90 | ![Screenshot 7](https://i.imgur.com/wKKxrFj.png) 91 | 92 | ![Screenshot 8](https://i.imgur.com/CvfuGic.png) 93 | -------------------------------------------------------------------------------- /XTulator/chipset/i8237.c: -------------------------------------------------------------------------------- 1 | /* 2 | XTulator: A portable, open-source 8086 PC emulator. 3 | Copyright (C)2020 Mike Chambers 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | /* 21 | Intel 8237 DMA controller 22 | */ 23 | 24 | #include "../config.h" 25 | #include 26 | #include 27 | #include 28 | #include "i8237.h" 29 | #include "../cpu/cpu.h" 30 | #include "../ports.h" 31 | #include "../timing.h" 32 | #include "../debuglog.h" 33 | 34 | void i8237_reset(I8237_t* i8237) { 35 | memset(i8237, 0, sizeof(I8237_t)); 36 | i8237->chan[0].masked = 1; 37 | i8237->chan[1].masked = 1; 38 | i8237->chan[2].masked = 1; 39 | i8237->chan[3].masked = 1; 40 | } 41 | 42 | void i8237_writeport(I8237_t* i8237, uint16_t addr, uint8_t value) { 43 | uint8_t ch; 44 | #ifdef DEBUG_DMA 45 | debug_log(DEBUG_DETAIL, "[DMA] Write port 0x%X: %X\n", addr, value); 46 | #endif 47 | addr &= 0x0F; 48 | switch (addr) { 49 | case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: 50 | ch = (addr >> 1) & 3; 51 | if (addr & 0x01) { //write terminal count 52 | if (i8237->flipflop) { 53 | i8237->chan[ch].count = (i8237->chan[ch].count & 0x00FF) | ((uint16_t)value << 8); 54 | } 55 | else { 56 | i8237->chan[ch].count = (i8237->chan[ch].count & 0xFF00) | (uint16_t)value; 57 | } 58 | i8237->chan[ch].reloadcount = i8237->chan[ch].count; 59 | } 60 | else { 61 | if (i8237->flipflop) { 62 | i8237->chan[ch].addr = (i8237->chan[ch].addr & 0x00FF) | ((uint16_t)value << 8); 63 | } 64 | else { 65 | i8237->chan[ch].addr = (i8237->chan[ch].addr & 0xFF00) | (uint16_t)value; 66 | } 67 | i8237->chan[ch].reloadaddr = i8237->chan[ch].addr; 68 | #ifdef DEBUG_DMA 69 | debug_log(DEBUG_DETAIL, "[DMA] Channel %u addr set to %08X\r\n", ch, i8237->chan[ch].addr); 70 | #endif 71 | } 72 | i8237->flipflop ^= 1; 73 | break; 74 | case 0x08: //DMA channel 0-3 command register 75 | i8237->memtomem = value & 1; 76 | break; 77 | case 0x09: //DMA request register 78 | i8237->chan[value & 3].dreq = (value >> 2) & 1; 79 | break; 80 | case 0x0A: //DMA channel 0-3 mask register 81 | i8237->chan[value & 3].masked = (value >> 2) & 1; 82 | break; 83 | case 0x0B: //DMA channel 0-3 mode register 84 | i8237->chan[value & 3].operation = (value >> 2) & 3; 85 | i8237->chan[value & 3].mode = (value >> 6) & 3; 86 | i8237->chan[value & 3].autoinit = (value >> 4) & 1; 87 | i8237->chan[value & 3].addrinc = (value & 0x20) ? 0xFFFFFFFF : 0x00000001; 88 | break; 89 | case 0x0C: //clear byte pointer flip flop 90 | i8237->flipflop = 0; 91 | break; 92 | case 0x0D: //DMA master clear 93 | i8237_reset(i8237); 94 | break; 95 | case 0x0F: //DMA write mask register 96 | i8237->chan[0].masked = value & 1; 97 | i8237->chan[1].masked = (value >> 1) & 1; 98 | i8237->chan[2].masked = (value >> 2) & 1; 99 | i8237->chan[3].masked = (value >> 3) & 1; 100 | break; 101 | } 102 | } 103 | 104 | void i8237_writepage(I8237_t* i8237, uint16_t addr, uint8_t value) { 105 | uint8_t ch; 106 | #ifdef DEBUG_DMA 107 | debug_log(DEBUG_DETAIL, "[DMA] Write port 0x%X: %X\n", addr, value); 108 | #endif 109 | addr &= 0x0F; 110 | switch (addr) { 111 | case 0x07: 112 | ch = 0; 113 | break; 114 | case 0x03: 115 | ch = 1; 116 | break; 117 | case 0x01: 118 | ch = 2; 119 | break; 120 | case 0x02: 121 | ch = 3; 122 | break; 123 | default: 124 | return; 125 | } 126 | i8237->chan[ch].page = (uint32_t)value << 16; 127 | #ifdef DEBUG_DMA 128 | debug_log(DEBUG_DETAIL, "[DMA] Channel %u page set to %08X\r\n", ch, i8237->chan[ch].page); 129 | #endif 130 | } 131 | 132 | uint8_t i8237_readport(I8237_t* i8237, uint16_t addr) { 133 | uint8_t ch, ret = 0xFF; 134 | #ifdef DEBUG_DMA 135 | debug_log(DEBUG_DETAIL, "[DMA] Read port 0x%X\n", addr); 136 | #endif 137 | 138 | addr &= 0x0F; 139 | switch (addr) { 140 | case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: 141 | ch = (addr >> 1) & 3; 142 | if (addr & 1) { //count 143 | if (i8237->flipflop) { 144 | ret = (uint8_t)(i8237->chan[ch].count >> 8); //TODO: or give back the reload?? 145 | } 146 | else { 147 | ret = (uint8_t)i8237->chan[ch].count; //TODO: or give back the reload?? 148 | } 149 | } else { //address 150 | //printf("%04X\r\n", i8237->chan[ch].addr); 151 | if (i8237->flipflop) { 152 | ret = (uint8_t)(i8237->chan[ch].addr >> 8); 153 | } 154 | else { 155 | ret = (uint8_t)i8237->chan[ch].addr; 156 | } 157 | } 158 | i8237->flipflop ^= 1; 159 | break; 160 | case 0x08: //status register 161 | ret = 0x0F; 162 | } 163 | return ret; 164 | } 165 | 166 | uint8_t i8237_readpage(I8237_t* i8237, uint16_t addr) { 167 | uint8_t ch; 168 | #ifdef DEBUG_DMA 169 | debug_log(DEBUG_DETAIL, "[DMA] Read port 0x%X\n", addr); 170 | #endif 171 | addr &= 0x0F; 172 | switch (addr) { 173 | case 0x07: 174 | ch = 0; 175 | break; 176 | case 0x03: 177 | ch = 1; 178 | break; 179 | case 0x01: 180 | ch = 2; 181 | break; 182 | case 0x02: 183 | ch = 3; 184 | break; 185 | default: 186 | return 0xFF; 187 | } 188 | return (uint8_t)(i8237->chan[ch].page >> 16); 189 | } 190 | 191 | uint8_t i8237_read(I8237_t* i8237, uint8_t ch) { 192 | uint8_t ret = 0xFF; 193 | 194 | //TODO: fix commented out stuff 195 | //if (i8237->chan[ch].enable && !i8237->chan[ch].terminal) { 196 | ret = cpu_read(i8237->cpu, i8237->chan[ch].page + i8237->chan[ch].addr); 197 | i8237->chan[ch].addr += i8237->chan[ch].addrinc; 198 | i8237->chan[ch].count--; 199 | if (i8237->chan[ch].count == 0xFFFF) { 200 | if (i8237->chan[ch].autoinit) { 201 | i8237->chan[ch].count = i8237->chan[ch].reloadcount; 202 | i8237->chan[ch].addr = i8237->chan[ch].reloadaddr; 203 | } else { 204 | i8237->chan[ch].terminal = 1; //TODO: does this also happen in autoinit mode? 205 | } 206 | } 207 | //} 208 | 209 | return ret; 210 | } 211 | 212 | void i8237_write(I8237_t* i8237, uint8_t ch, uint8_t value) { 213 | //TODO: fix commented out stuff 214 | //if (i8237->chan[ch].enable && !i8237->chan[ch].terminal) { 215 | cpu_write(i8237->cpu, i8237->chan[ch].page + i8237->chan[ch].addr, value); 216 | printf("Write to %05X\r\n", i8237->chan[ch].page + i8237->chan[ch].addr); 217 | i8237->chan[ch].addr += i8237->chan[ch].addrinc; 218 | i8237->chan[ch].count--; 219 | if (i8237->chan[ch].count == 0xFFFF) { 220 | if (i8237->chan[ch].autoinit) { 221 | i8237->chan[ch].count = i8237->chan[ch].reloadcount; 222 | i8237->chan[ch].addr = i8237->chan[ch].reloadaddr; 223 | } 224 | else { 225 | i8237->chan[ch].terminal = 1; //TODO: does this also happen in autoinit mode? 226 | } 227 | } 228 | } 229 | 230 | void i8237_init(I8237_t* i8237, CPU_t* cpu) { 231 | i8237_reset(i8237); 232 | 233 | ports_cbRegister(0x00, 16, (void*)i8237_readport, NULL, (void*)i8237_writeport, NULL, i8237); 234 | ports_cbRegister(0x80, 16, (void*)i8237_readpage, NULL, (void*)i8237_writepage, NULL, i8237); 235 | } 236 | -------------------------------------------------------------------------------- /XTulator/modules/io/ne2000.h: -------------------------------------------------------------------------------- 1 | #ifndef _NE2000_H_ 2 | #define _NE2000_H_ 3 | 4 | #include 5 | #include "../../chipset/i8259.h" 6 | 7 | #define NE2K_MEMSIZ (32*1024) 8 | #define NE2K_MEMSTART (16*1024) 9 | #define NE2K_MEMEND (NE2K_MEMSTART + NE2K_MEMSIZ) 10 | 11 | typedef struct 12 | { 13 | // 14 | // ne2k register state 15 | 16 | // 17 | // Page 0 18 | // 19 | // Command Register - 00h read/write 20 | struct CR_t { 21 | int stop; // STP - Software Reset command 22 | int start; // START - start the NIC 23 | int tx_packet; // TXP - initiate packet transmission 24 | uint8_t rdma_cmd; // RD0,RD1,RD2 - Remote DMA command 25 | uint8_t pgsel; // PS0,PS1 - Page select 26 | } CR; 27 | // Interrupt Status Register - 07h read/write 28 | struct ISR_t { 29 | int pkt_rx; // PRX - packet received with no errors 30 | int pkt_tx; // PTX - packet transmitted with no errors 31 | int rx_err; // RXE - packet received with 1 or more errors 32 | int tx_err; // TXE - packet tx'd " " " " " 33 | int overwrite; // OVW - rx buffer resources exhausted 34 | int cnt_oflow; // CNT - network tally counter MSB's set 35 | int rdma_done; // RDC - remote DMA complete 36 | int reset; // RST - reset status 37 | } ISR; 38 | // Interrupt Mask Register - 0fh write 39 | struct IMR_t { 40 | int rx_inte; // PRXE - packet rx interrupt enable 41 | int tx_inte; // PTXE - packet tx interrput enable 42 | int rxerr_inte; // RXEE - rx error interrupt enable 43 | int txerr_inte; // TXEE - tx error interrupt enable 44 | int overw_inte; // OVWE - overwrite warn int enable 45 | int cofl_inte; // CNTE - counter o'flow int enable 46 | int rdma_inte; // RDCE - remote DMA complete int enable 47 | int reserved; // D7 - reserved 48 | } IMR; 49 | // Data Configuration Register - 0eh write 50 | struct DCR_t { 51 | int wdsize; // WTS - 8/16-bit select 52 | int endian; // BOS - byte-order select 53 | int longaddr; // LAS - long-address select 54 | int loop; // LS - loopback select 55 | int auto_rx; // AR - auto-remove rx packets with remote DMA 56 | uint8_t fifo_size; // FT0,FT1 - fifo threshold 57 | } DCR; 58 | // Transmit Configuration Register - 0dh write 59 | struct TCR_t { 60 | int crc_disable; // CRC - inhibit tx CRC 61 | uint8_t loop_cntl; // LB0,LB1 - loopback control 62 | int ext_stoptx; // ATD - allow tx disable by external mcast 63 | int coll_prio; // OFST - backoff algorithm select 64 | uint8_t reserved; // D5,D6,D7 - reserved 65 | } TCR; 66 | // Transmit Status Register - 04h read 67 | struct TSR_t { 68 | int tx_ok; // PTX - tx complete without error 69 | int reserved; // D1 - reserved 70 | int collided; // COL - tx collided >= 1 times 71 | int aborted; // ABT - aborted due to excessive collisions 72 | int no_carrier; // CRS - carrier-sense lost 73 | int fifo_ur; // FU - FIFO underrun 74 | int cd_hbeat; // CDH - no tx cd-heartbeat from transceiver 75 | int ow_coll; // OWC - out-of-window collision 76 | } TSR; 77 | // Receive Configuration Register - 0ch write 78 | struct RCR_t { 79 | int errors_ok; // SEP - accept pkts with rx errors 80 | int runts_ok; // AR - accept < 64-byte runts 81 | int broadcast; // AB - accept eth broadcast address 82 | int multicast; // AM - check mcast hash array 83 | int promisc; // PRO - accept all packets 84 | int monitor; // MON - check pkts, but don't rx 85 | uint8_t reserved; // D6,D7 - reserved 86 | } RCR; 87 | // Receive Status Register - 0ch read 88 | struct RSR_t { 89 | int rx_ok; // PRX - rx complete without error 90 | int bad_crc; // CRC - Bad CRC detected 91 | int bad_falign; // FAE - frame alignment error 92 | int fifo_or; // FO - FIFO overrun 93 | int rx_missed; // MPA - missed packet error 94 | int rx_mbit; // PHY - unicast or mcast/bcast address match 95 | int rx_disabled; // DIS - set when in monitor mode 96 | int deferred; // DFR - collision active 97 | } RSR; 98 | 99 | uint16_t local_dma; // 01,02h read ; current local DMA addr 100 | uint8_t page_start; // 01h write ; page start register 101 | uint8_t page_stop; // 02h write ; page stop register 102 | uint8_t bound_ptr; // 03h read/write ; boundary pointer 103 | uint8_t tx_page_start; // 04h write ; transmit page start register 104 | uint8_t num_coll; // 05h read ; number-of-collisions register 105 | uint16_t tx_bytes; // 05,06h write ; transmit byte-count register 106 | uint8_t fifo; // 06h read ; FIFO 107 | uint16_t remote_dma; // 08,09h read ; current remote DMA addr 108 | uint16_t remote_start; // 08,09h write ; remote start address register 109 | uint16_t remote_bytes; // 0a,0bh write ; remote byte-count register 110 | uint8_t tallycnt_0; // 0dh read ; tally counter 0 (frame align errors) 111 | uint8_t tallycnt_1; // 0eh read ; tally counter 1 (CRC errors) 112 | uint8_t tallycnt_2; // 0fh read ; tally counter 2 (missed pkt errors) 113 | 114 | uint8_t i8029id0; 115 | uint8_t i8029id1; 116 | 117 | // 118 | // Page 1 119 | // 120 | // Command Register 00h (repeated) 121 | // 122 | uint8_t physaddr[6]; // 01-06h read/write ; MAC address 123 | uint8_t curr_page; // 07h read/write ; current page register 124 | uint8_t mchash[8]; // 08-0fh read/write ; multicast hash array 125 | 126 | // 127 | // Page 2 - diagnostic use only 128 | // 129 | // Command Register 00h (repeated) 130 | // 131 | // Page Start Register 01h read (repeated) 132 | // Page Stop Register 02h read (repeated) 133 | // Current Local DMA Address 01,02h write (repeated) 134 | // Transmit Page start address 04h read (repeated) 135 | // Receive Configuration Register 0ch read (repeated) 136 | // Transmit Configuration Register 0dh read (repeated) 137 | // Data Configuration Register 0eh read (repeated) 138 | // Interrupt Mask Register 0fh read (repeated) 139 | // 140 | uint8_t rempkt_ptr; // 03h read/write ; remote next-packet pointer 141 | uint8_t localpkt_ptr; // 05h read/write ; local next-packet pointer 142 | uint16_t address_cnt; // 06,07h read/write ; address counter 143 | 144 | // 145 | // Page 3 - should never be modified. 146 | // 147 | uint8_t cr; 148 | uint8_t i9346cr; 149 | uint8_t config0; 150 | uint8_t config2; 151 | uint8_t config3; 152 | uint8_t hltclk; 153 | uint8_t i8029asid0; 154 | uint8_t i8029asid1; 155 | 156 | // Novell ASIC state 157 | uint8_t macaddr[32]; // ASIC ROM'd MAC address, even bytes 158 | uint8_t mem[NE2K_MEMSIZ]; // on-chip packet memory 159 | 160 | // ne2k internal state 161 | uint32_t base_address; 162 | int base_irq; 163 | int tx_timer_index; 164 | int tx_timer_active; 165 | 166 | //XTulator stuff 167 | I8259_t* i8259; 168 | uint32_t tx_timer; 169 | 170 | } NE2000_t; 171 | 172 | void ne2000_init(NE2000_t* ne2000, I8259_t* i8259, uint32_t baseport, uint8_t irq, uint8_t* macaddr); 173 | void ne2000_rx_frame(NE2000_t* ne2000, const void* buf, int io_len); 174 | void NE2000_tx_event(NE2000_t* ne2000, uint64_t interval); 175 | void NE2000_tx_timer(NE2000_t* ne2000); 176 | 177 | #endif -------------------------------------------------------------------------------- /XTulator/modules/audio/opl2.c: -------------------------------------------------------------------------------- 1 | /* 2 | XTulator: A portable, open-source 80186 PC emulator. 3 | Copyright (C)2020 Mike Chambers 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include "../../config.h" 25 | #include "../../debuglog.h" 26 | #include "../../ports.h" 27 | #include "../../timing.h" 28 | #include "opl2.h" 29 | 30 | const double opl2_attack[16] = { 31 | 1.004, 1.005, 1.006, 1.007, 1.008, 1.009, 1.01, 1.02, 32 | 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.09, 1.1 33 | }; 34 | 35 | const double opl2_decay[16] = { 36 | 0.99995, 0.9998, 0.9997, 0.9996, 0.9995, 0.9994, 0.9993, 0.9992, 37 | 0.9990, 0.9989, 0.9988, 0.9987, 0.9986, 0.9985, 0.9984, 0.9983 38 | }; 39 | 40 | /*const double opl2_modfreq[16] = { 41 | 42 | };*/ 43 | 44 | const double opl2_suslevel[16] = { 45 | 0.75, 0.70, 0.65, 0.60, 0.55, 0.45, 0.40, 0.35, 0.30, 0.25, 0.20, 0.15, 0.10, 0.05, 0.0025 46 | }; 47 | 48 | const uint8_t opregoffset[22] = { 49 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15 50 | }; 51 | 52 | const uint8_t chanopnum[9][2] = { 53 | { 0x00, 0x03 }, 54 | { 0x01, 0x04 }, 55 | { 0x02, 0x05 }, 56 | { 0x08, 0x0B }, 57 | { 0x09, 0x0C }, 58 | { 0x0A, 0x0D }, 59 | { 0x10, 0x13 }, 60 | { 0x11, 0x14 }, 61 | { 0x12, 0x15 } 62 | }; 63 | 64 | const uint8_t optochan[0x16] = { 65 | 1, 2, 3, 1, 2, 3, 255, 255, 4, 5, 6, 4, 5, 6, 255, 255, 7, 8, 9, 7, 8, 9 66 | }; 67 | 68 | void opl2_writeData(OPL2_t* opl2, uint8_t value) { 69 | uint8_t ch, op, op1, op2; 70 | if ((opl2->addr >= 0x20) && (opl2->addr <= 0x35)) { 71 | op = opl2->addr - 0x20; 72 | opl2->oper[op].sustain = (value & 0x20) ? 1 : 0; 73 | opl2->oper[op].usevibrato = (value & 0x40) ? 1 : 0; //TODO: actually implement this in the operator ticks 74 | opl2->oper[op].usetremolo = (value & 0x80) ? 1 : 0; //TODO: actually implement this in the operator ticks 75 | } 76 | else if ((opl2->addr >= 0x40) && (opl2->addr <= 0x55)) { 77 | op = opl2->addr - 0x40; 78 | opl2->oper[op].volume = 18; // value & 0x3F; //TODO: fix volume 79 | } 80 | else if ((opl2->addr >= 0x80) && (opl2->addr <= 0x95)) { 81 | op = opl2->addr - 0x80; 82 | opl2->oper[op].sustainlevel = value >> 4; 83 | //TODO: release rate 84 | } 85 | else if ((opl2->addr >= 0xA0) && (opl2->addr <= 0xA8)) { 86 | ch = opl2->addr - 0xA0; 87 | opl2->chan[ch].fnum = (opl2->chan[ch].fnum & 0xFF00) | value; 88 | } 89 | else if ((opl2->addr >= 0xB0) && (opl2->addr <= 0xB8)) { 90 | ch = opl2->addr - 0xB0; 91 | op1 = chanopnum[ch][0]; 92 | op2 = chanopnum[ch][1]; 93 | opl2->chan[ch].fnum = (opl2->chan[ch].fnum & 0x00FF) | ((uint16_t)(value & 3) << 8); 94 | opl2->chan[ch].octave = (value >> 2) & 7; 95 | opl2->chan[ch].frequency = 2 * pow(2, opl2->chan[ch].octave) * (49716.0 / 1048576.0) * (double)opl2->chan[ch].fnum; 96 | if ((opl2->chan[ch].on == 0) && (value & 0x20)) { 97 | opl2->chan[ch].on = 1; 98 | opl2->oper[op1].inattack = 1; 99 | opl2->oper[op1].attackval = 0; 100 | opl2->oper[op1].decayval = 0; 101 | opl2->oper[op1].envelope = 0.01; 102 | timing_timerEnable(opl2->oper[op1].timer); 103 | #ifdef DEBUG_OPL2 104 | debug_log(DEBUG_DETAIL, "[OPL2] Key on channel %u, frequency: %f\r\n", ch, opl2->chan[ch].frequency); 105 | #endif 106 | } 107 | else { 108 | opl2->chan[ch].on = (value & 0x20) ? 1 : 0; 109 | opl2->oper[op1].amplitude = 0; 110 | opl2->oper[op2].amplitude = 0; 111 | timing_timerDisable(opl2->oper[op1].timer); 112 | timing_timerDisable(opl2->oper[op2].timer); 113 | } 114 | } 115 | else if ((opl2->addr >= 0xE0) && (opl2->addr <= 0xF5)) { 116 | op = opl2->addr - 0xE0; 117 | opl2->oper[op].waveform = value & 3; 118 | #ifdef DEBUG_OPL2 119 | debug_log(DEBUG_DETAIL, "[OPL2] Oper %u waveform set to %u\r\n", op, value & 3); 120 | #endif 121 | } 122 | } 123 | 124 | void opl2_write(OPL2_t* opl2, uint16_t portnum, uint8_t value) { 125 | #ifdef DEBUG_OPL2 126 | debug_log(DEBUG_DETAIL, "[OPL2] Write %03X: %u\r\n", portnum, value); 127 | #endif 128 | 129 | portnum &= 1; 130 | switch (portnum) { 131 | case 0: //address 132 | opl2->addr = value; 133 | break; 134 | case 1: //data 135 | opl2->data[opl2->addr] = value; 136 | opl2_writeData(opl2, value); 137 | break; 138 | } 139 | } 140 | 141 | uint8_t opl2_read(OPL2_t* opl2, uint16_t portnum) { 142 | #ifdef DEBUG_OPL2 143 | debug_log(DEBUG_DETAIL, "[OPL2] Read %03X\r\n", portnum); 144 | #endif 145 | portnum &= 1; 146 | if (portnum == 0) { //status port 147 | uint8_t ret; 148 | ret = (opl2->data[0x04] & 0x01) ? 0x40 : 0x00; 149 | ret |= (opl2->data[0x04] & 0x02) ? 0x20 : 0x00; 150 | ret |= ret ? 0x80 : 0x00; 151 | return ret; 152 | } 153 | else { 154 | return 0xFF; 155 | } 156 | } 157 | 158 | int16_t opl2_generateSample(OPL2_t* opl2) { 159 | uint16_t op; 160 | //int16_t tmp; 161 | double val = 0; 162 | //static FILE* wavout = NULL; 163 | 164 | //if (wavout == NULL) { 165 | // wavout = fopen("audio.raw", "wb"); 166 | //} 167 | 168 | for (op = 0; op < 0x16; op++) { 169 | if (optochan[op] != 255) { 170 | val += opl2->oper[op].sample; 171 | } 172 | } 173 | 174 | //printf("%f\r\n", val); 175 | 176 | //tmp = (int16_t)val; 177 | //fwrite(&tmp, 2, 1, wavout); 178 | 179 | return (int16_t)val; 180 | } 181 | 182 | void opl2_tickOperator(OPL2CB_t* opl2cb) { 183 | OPL2_t* opl2; 184 | uint8_t chan, op; 185 | double mult, sine; 186 | 187 | opl2 = opl2cb->opl2; 188 | op = opl2cb->op; 189 | chan = optochan[op]; 190 | if (chan == 255) return; 191 | chan--; 192 | 193 | sine = sin(opl2->chan[chan].frequency * 6.283185307179586 * (double)opl2->oper[op].tick / SAMPLE_RATE); 194 | mult = 1.0; 195 | switch (opl2->oper[op].waveform) { 196 | case 1: 197 | if (sine < 0) mult = 0.0; 198 | break; 199 | case 2: 200 | if (sine < 0) mult = -1.0; 201 | break; 202 | case 3: 203 | if ((sine < opl2->oper[op].lastsine) || (sine < 0)) mult = 0.0; 204 | break; 205 | } 206 | opl2->oper[op].amplitude = opl2->oper[op].envelope * pow(VOLUME_CONST, (double)(63 - opl2->oper[op].volume)); 207 | opl2->oper[op].sample = mult * opl2->oper[op].amplitude * sine; 208 | opl2->oper[op].tick = (opl2->oper[op].tick + 1) % SAMPLE_RATE; 209 | opl2->oper[op].lastsine = sine; 210 | 211 | if (opl2->oper[op].inattack) { 212 | opl2->oper[op].envelope *= opl2_attack[opl2->oper[op].attackval]; 213 | if (opl2->oper[op].envelope >= 1.0) { 214 | opl2->oper[op].inattack = 0; 215 | } 216 | } else { 217 | opl2->oper[op].envelope *= opl2_decay[opl2->oper[op].decayval]; 218 | if (opl2->oper[op].sustain) { 219 | //TODO: I'm doing this completely wrong... 220 | if (opl2->oper[op].envelope < opl2_suslevel[opl2->oper[op].sustainlevel]) { 221 | opl2->oper[op].envelope = opl2_suslevel[opl2->oper[op].sustainlevel]; 222 | } 223 | } 224 | } 225 | } 226 | 227 | void opl2_init(OPL2_t* opl2) { 228 | uint8_t i; 229 | memset(opl2, 0, sizeof(OPL2_t)); 230 | ports_cbRegister(0x388, 2, (void*)opl2_read, NULL, (void*)opl2_write, NULL, opl2); 231 | 232 | for (i = 0; i < 0x16; i++) { 233 | //TODO: add error handling 234 | //opl2->oper[i].opdata.chan = i; 235 | opl2->oper[i].opdata.op = i; 236 | opl2->oper[i].opdata.opl2 = (void*)opl2; 237 | opl2->oper[i].timer = timing_addTimer(opl2_tickOperator, &opl2->oper[i].opdata, SAMPLE_RATE, TIMING_DISABLED); 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /XTulator/menus.c: -------------------------------------------------------------------------------- 1 | /* 2 | XTulator: A portable, open-source 80186 PC emulator. 3 | Copyright (C)2020 Mike Chambers 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | #ifdef _WIN32 21 | 22 | #include 23 | #include 24 | #include 25 | #include "machine.h" 26 | #include 27 | #include 28 | #include "config.h" 29 | #include "modules/disk/biosdisk.h" 30 | #include "timing.h" 31 | #include "utility.h" 32 | #include "menus.h" 33 | 34 | WNDPROC menus_oldProc; 35 | MACHINE_t* menus_useMachine = NULL; 36 | uint32_t menus_resetTimer; 37 | const uint8_t menus_ctrlaltdel[3] = { 0x1D, 0x38, 0x53 }; 38 | uint8_t menus_resetPos = 0; 39 | 40 | MENU_t menu_file[] = { 41 | { TEXT("Soft &reset (Ctrl-Alt-Del)"), MENUS_ENABLED, MENUS_FUNCTION, (void*)menus_reset }, 42 | { TEXT(""), MENUS_ENABLED, MENUS_SEPARATOR, NULL }, 43 | { TEXT("E&xit"), MENUS_ENABLED, MENUS_FUNCTION, (void*)menus_exit }, 44 | { NULL } 45 | }; 46 | 47 | MENU_t menu_disk[] = { 48 | { TEXT("Change floppy 0..."), MENUS_ENABLED, MENUS_FUNCTION, (void*)menus_changeFloppy0 }, 49 | { TEXT("Change floppy 1..."), MENUS_ENABLED, MENUS_FUNCTION, (void*)menus_changeFloppy1 }, 50 | { TEXT(""), MENUS_ENABLED, MENUS_SEPARATOR, NULL }, 51 | { TEXT("Eject floppy 0"), MENUS_ENABLED, MENUS_FUNCTION, (void*)menus_ejectFloppy0 }, 52 | { TEXT("Eject floppy 1"), MENUS_ENABLED, MENUS_FUNCTION, (void*)menus_ejectFloppy1 }, 53 | { TEXT(""), MENUS_ENABLED, MENUS_SEPARATOR, NULL }, 54 | { TEXT("Insert hard disk 0... (forces immediate reboot)"), MENUS_ENABLED, MENUS_FUNCTION, (void*)menus_insertHard0 }, 55 | { TEXT("Insert hard disk 1... (forces immediate reboot)"), MENUS_ENABLED, MENUS_FUNCTION, (void*)menus_insertHard1 }, 56 | { TEXT(""), MENUS_ENABLED, MENUS_SEPARATOR, NULL }, 57 | { TEXT("Set boot drive to fd0"), MENUS_ENABLED, MENUS_FUNCTION, (void*)menus_setBootFloppy0 }, 58 | { TEXT("Set boot drive to hd0"), MENUS_ENABLED, MENUS_FUNCTION, (void*)menus_setBootHard0 }, 59 | { NULL } 60 | }; 61 | 62 | MENU_t menu_emulation[] = { 63 | { TEXT("Set CPU speed to 4.77 MHz"), MENUS_ENABLED, MENUS_FUNCTION, (void*)menus_speed477 }, 64 | { TEXT("Set CPU speed to 8 MHz"), MENUS_ENABLED, MENUS_FUNCTION, (void*)menus_speed8 }, 65 | { TEXT("Set CPU speed to 10 MHz"), MENUS_ENABLED, MENUS_FUNCTION, (void*)menus_speed10 }, 66 | { TEXT("Set CPU speed to 16 MHz"), MENUS_ENABLED, MENUS_FUNCTION, (void*)menus_speed16 }, 67 | { TEXT("Set CPU speed to 25 MHz"), MENUS_ENABLED, MENUS_FUNCTION, (void*)menus_speed25 }, 68 | { TEXT("Set CPU speed to 50 MHz"), MENUS_ENABLED, MENUS_FUNCTION, (void*)menus_speed50 }, 69 | { TEXT("Set CPU speed to unlimited"), MENUS_ENABLED, MENUS_FUNCTION, (void*)menus_speedunlimited }, 70 | { NULL } 71 | }; 72 | 73 | 74 | LRESULT CALLBACK menus_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { 75 | void (*func)(); 76 | 77 | switch (msg) { 78 | case WM_COMMAND: 79 | func = (void*)wParam; 80 | if (func != NULL) { 81 | (*func)(); 82 | } 83 | return(TRUE); 84 | } 85 | 86 | return(CallWindowProc(menus_oldProc, hwnd, msg, wParam, lParam)); 87 | } 88 | 89 | int menus_buildMenu(HMENU* hmenuBar, wchar_t* title, MENU_t* menu) { 90 | HMENU hmenu; 91 | UINT i; 92 | 93 | hmenu = CreateMenu(); 94 | i = 0; 95 | while (menu[i].title != NULL) { 96 | if (menu[i].type == MENUS_FUNCTION) { 97 | AppendMenuW(hmenu, MF_STRING, (UINT)menu[i].function, menu[i].title); 98 | } 99 | else if (menu[i].type == MENUS_SEPARATOR) { 100 | AppendMenuW(hmenu, MF_SEPARATOR, 0, NULL); 101 | } 102 | i++; 103 | } 104 | AppendMenuW(*hmenuBar, MF_POPUP, (UINT_PTR)hmenu, title); 105 | 106 | return 0; 107 | } 108 | 109 | void menus_resetCallback(void* dummy) { 110 | menus_useMachine->KeyState.scancode = menus_ctrlaltdel[menus_resetPos++]; 111 | menus_useMachine->KeyState.isNew = 1; 112 | i8259_doirq(&menus_useMachine->i8259, 1); 113 | 114 | if (menus_resetPos == 3) { 115 | timing_timerDisable(menus_resetTimer); 116 | } 117 | } 118 | 119 | int menus_init(HWND hwnd) { 120 | HMENU hmenuBar; 121 | 122 | hmenuBar = CreateMenu(); 123 | 124 | if (menus_buildMenu(&hmenuBar, TEXT("File"), menu_file) < 0) { 125 | return -1; 126 | } 127 | if (menus_buildMenu(&hmenuBar, TEXT("Emulation"), menu_emulation) < 0) { 128 | return -1; 129 | } 130 | if (menus_buildMenu(&hmenuBar, TEXT("Disk"), menu_disk) < 0) { 131 | return -1; 132 | } 133 | 134 | SetMenu(hwnd, hmenuBar); 135 | DrawMenuBar(hwnd); 136 | 137 | menus_oldProc = (WNDPROC)SetWindowLong(hwnd, GWL_WNDPROC, (LONG_PTR)menus_wndProc); 138 | menus_resetTimer = timing_addTimer(menus_resetCallback, NULL, 10, TIMING_DISABLED); 139 | 140 | return 0; 141 | } 142 | 143 | void menus_setMachine(MACHINE_t* machine) { 144 | menus_useMachine = machine; 145 | } 146 | 147 | //Below is all code for each menu item 148 | 149 | void menus_exit() { 150 | running = 0; 151 | } 152 | 153 | void menus_openFloppyFile(uint8_t disk) { 154 | OPENFILENAME of_dlg; 155 | wchar_t filename[MAX_PATH + 1] = { 0 }; 156 | 157 | memset(&of_dlg, 0, sizeof(of_dlg)); 158 | of_dlg.lStructSize = sizeof(of_dlg); 159 | of_dlg.lpstrTitle = TEXT("Open disk image"); 160 | of_dlg.hInstance = NULL; 161 | of_dlg.lpstrFile = filename; 162 | of_dlg.lpstrFilter = TEXT("Floppy disk images (*.img)\0*.img\0All files (*.*)\0*.*\0\0"); 163 | of_dlg.nMaxFile = MAX_PATH; 164 | of_dlg.Flags = OFN_FILEMUSTEXIST | OFN_LONGNAMES; 165 | if (GetOpenFileName(&of_dlg)) { 166 | size_t size; 167 | char* filembs; 168 | size = wcstombs(NULL, of_dlg.lpstrFile, 0); 169 | filembs = (char*)malloc(size + 1); 170 | if (filembs == NULL) { 171 | return; 172 | } 173 | wcstombs(filembs, of_dlg.lpstrFile, size + 1); 174 | biosdisk_insert(&menus_useMachine->CPU, disk, filembs); 175 | free(filembs); 176 | } 177 | } 178 | 179 | void menus_openHardFile(uint8_t disk) { 180 | OPENFILENAME of_dlg; 181 | wchar_t filename[MAX_PATH + 1] = { 0 }; 182 | 183 | memset(&of_dlg, 0, sizeof(of_dlg)); 184 | of_dlg.lStructSize = sizeof(of_dlg); 185 | of_dlg.lpstrTitle = TEXT("Open disk image"); 186 | of_dlg.hInstance = NULL; 187 | of_dlg.lpstrFile = filename; 188 | of_dlg.lpstrFilter = TEXT("Hard disk images (*.img)\0*.img\0All files (*.*)\0*.*\0\0"); 189 | of_dlg.nMaxFile = MAX_PATH; 190 | of_dlg.Flags = OFN_FILEMUSTEXIST | OFN_LONGNAMES; 191 | if (GetOpenFileName(&of_dlg)) { 192 | size_t size; 193 | char* filembs; 194 | size = wcstombs(NULL, of_dlg.lpstrFile, 0); 195 | filembs = (char*)malloc(size + 1); 196 | if (filembs == NULL) { 197 | return; 198 | } 199 | wcstombs(filembs, of_dlg.lpstrFile, size + 1); 200 | biosdisk_insert(&menus_useMachine->CPU, disk, filembs); 201 | free(filembs); 202 | menus_reset(); 203 | } 204 | } 205 | 206 | void menus_changeFloppy0() { 207 | menus_openFloppyFile(0); 208 | } 209 | 210 | void menus_changeFloppy1() { 211 | menus_openFloppyFile(1); 212 | } 213 | 214 | void menus_ejectFloppy0() { 215 | biosdisk_eject(&menus_useMachine->CPU, 0); 216 | } 217 | 218 | void menus_ejectFloppy1() { 219 | biosdisk_eject(&menus_useMachine->CPU, 1); 220 | } 221 | 222 | void menus_insertHard0() { 223 | menus_openHardFile(2); 224 | } 225 | 226 | void menus_insertHard1() { 227 | menus_openHardFile(3); 228 | } 229 | 230 | void menus_setBootFloppy0() { 231 | bootdrive = 0; 232 | } 233 | 234 | void menus_setBootHard0() { 235 | bootdrive = 2; 236 | } 237 | 238 | void menus_reset() { 239 | menus_resetPos = 0; 240 | timing_timerEnable(menus_resetTimer); 241 | } 242 | 243 | void menus_speed477() { 244 | setspeed(4.77); 245 | } 246 | 247 | void menus_speed8() { 248 | setspeed(8.0); 249 | } 250 | 251 | void menus_speed10() { 252 | setspeed(10.0); 253 | } 254 | 255 | void menus_speed16() { 256 | setspeed(16.0); 257 | } 258 | 259 | void menus_speed25() { 260 | setspeed(25.0); 261 | } 262 | 263 | void menus_speed50() { 264 | setspeed(50.0); 265 | } 266 | 267 | void menus_speedunlimited() { 268 | setspeed(0); 269 | } 270 | 271 | #endif 272 | -------------------------------------------------------------------------------- /XTulator/modules/video/sdlconsole.c: -------------------------------------------------------------------------------- 1 | /* 2 | XTulator: A portable, open-source 80186 PC emulator. 3 | Copyright (C)2020 Mike Chambers 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | #include "../../config.h" 21 | 22 | #ifdef _WIN32 23 | #include 24 | #include 25 | #include 26 | #else 27 | #include 28 | #endif 29 | #include 30 | #include 31 | #include "sdlconsole.h" 32 | #include "../input/sdlkeys.h" 33 | #include "../input/mouse.h" 34 | #include "../../timing.h" 35 | #include "../../menus.h" 36 | 37 | SDL_Window *sdlconsole_window = NULL; 38 | SDL_Renderer *sdlconsole_renderer = NULL; 39 | SDL_Texture *sdlconsole_texture = NULL; 40 | 41 | uint64_t sdlconsole_frameTime[30]; 42 | uint32_t sdlconsole_keyTimer; 43 | uint8_t sdlconsole_curkey, sdlconsole_lastKey, sdlconsole_frameIdx = 0, sdlconsole_grabbed = 0, sdlconsole_ctrl = 0, sdlconsole_alt = 0, sdlconsole_doRepeat = 0; 44 | int sdlconsole_curw, sdlconsole_curh; 45 | 46 | char* sdlconsole_title; 47 | 48 | void sdlconsole_keyRepeat(void* dummy) { 49 | sdlconsole_doRepeat = 1; 50 | timing_updateIntervalFreq(sdlconsole_keyTimer, 15); 51 | } 52 | 53 | int sdlconsole_init(char *title) { 54 | #ifdef _WIN32 55 | HWND hwnd; 56 | SDL_SysWMinfo wmInfo; 57 | #endif 58 | 59 | if (SDL_Init(SDL_INIT_VIDEO)) return -1; 60 | 61 | sdlconsole_title = title; 62 | 63 | sdlconsole_window = SDL_CreateWindow(sdlconsole_title, 64 | SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 65 | 640, 400, 66 | SDL_WINDOW_OPENGL); 67 | if (sdlconsole_window == NULL) return -1; 68 | 69 | if (sdlconsole_setWindow(640, 400)) { 70 | return -1; 71 | } 72 | 73 | sdlconsole_keyTimer = timing_addTimer(sdlconsole_keyRepeat, NULL, 2, TIMING_DISABLED); 74 | 75 | #ifdef _WIN32 76 | SDL_VERSION(&wmInfo.version); 77 | SDL_GetWindowWMInfo(sdlconsole_window, &wmInfo); 78 | hwnd = wmInfo.info.win.window; 79 | menus_init(hwnd); 80 | #endif 81 | 82 | return 0; 83 | } 84 | 85 | int sdlconsole_setWindow(int w, int h) { 86 | if (sdlconsole_renderer != NULL) SDL_DestroyRenderer(sdlconsole_renderer); 87 | if (sdlconsole_texture != NULL) SDL_DestroyTexture(sdlconsole_texture); 88 | sdlconsole_renderer = NULL; 89 | sdlconsole_texture = NULL; 90 | 91 | SDL_SetWindowSize(sdlconsole_window, w, h); 92 | 93 | sdlconsole_renderer = SDL_CreateRenderer(sdlconsole_window, -1, 0); 94 | if (sdlconsole_renderer == NULL) return -1; 95 | 96 | sdlconsole_texture = SDL_CreateTexture(sdlconsole_renderer, 97 | SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 98 | w, h); 99 | if (sdlconsole_texture == NULL) return -1; 100 | 101 | sdlconsole_curw = w; 102 | sdlconsole_curh = h; 103 | 104 | return 0; 105 | } 106 | 107 | void sdlconsole_setTitle(char* title) { //appends something to the main title, doesn't replace it all 108 | char tmp[1024]; 109 | sprintf(tmp, "%s - %s", sdlconsole_title, title); 110 | SDL_SetWindowTitle(sdlconsole_window, tmp); 111 | } 112 | 113 | void sdlconsole_blit(uint32_t *pixels, int w, int h, int stride) { 114 | static uint64_t lasttime = 0; 115 | uint64_t curtime; 116 | curtime = timing_getCur(); 117 | 118 | if ((w != sdlconsole_curw) || (h != sdlconsole_curh)) { 119 | sdlconsole_setWindow(w, h); 120 | } 121 | SDL_UpdateTexture(sdlconsole_texture, NULL, pixels, stride); 122 | SDL_RenderClear(sdlconsole_renderer); 123 | SDL_RenderCopy(sdlconsole_renderer, sdlconsole_texture, NULL, NULL); 124 | SDL_RenderPresent(sdlconsole_renderer); 125 | 126 | if (lasttime != 0) { 127 | int i, avgcount; 128 | uint64_t curavg; 129 | char tmp[64]; 130 | sdlconsole_frameTime[sdlconsole_frameIdx++] = curtime - lasttime; 131 | if (sdlconsole_frameIdx == 30) { 132 | sdlconsole_frameIdx = 0; 133 | avgcount = 0; 134 | curavg = 0; 135 | for (i = 0; i < 30; i++) { 136 | if (sdlconsole_frameTime[i] != 0) { 137 | curavg += sdlconsole_frameTime[i]; 138 | avgcount++; 139 | } 140 | } 141 | curavg /= avgcount; 142 | sprintf(tmp, "%.2f FPS", (double)((timing_getFreq() * 10) / curavg) / 10); 143 | sdlconsole_setTitle(tmp); 144 | } 145 | } 146 | lasttime = curtime; 147 | } 148 | 149 | void sdlconsole_mousegrab() { 150 | sdlconsole_ctrl = sdlconsole_alt = 0; 151 | if (sdlconsole_grabbed) { 152 | SDL_SetRelativeMouseMode(SDL_FALSE); 153 | sdlconsole_grabbed = 0; 154 | } else { 155 | SDL_SetRelativeMouseMode(SDL_TRUE); 156 | sdlconsole_grabbed = 1; 157 | } 158 | } 159 | 160 | int sdlconsole_loop() { 161 | SDL_Event event; 162 | int8_t xrel, yrel; 163 | uint8_t action = 0; 164 | 165 | if (sdlconsole_doRepeat) { 166 | sdlconsole_doRepeat = 0; 167 | sdlconsole_curkey = sdlconsole_lastKey; 168 | return SDLCONSOLE_EVENT_KEY; 169 | } 170 | 171 | if (!SDL_PollEvent(&event)) return SDLCONSOLE_EVENT_NONE; 172 | switch (event.type) { 173 | case SDL_KEYDOWN: 174 | #ifdef DEBUG_VGA 175 | if (event.key.keysym.sym == SDLK_F12) { 176 | vga_dumpregs(); 177 | } 178 | #endif 179 | if (event.key.repeat) return SDLCONSOLE_EVENT_NONE; 180 | switch (event.key.keysym.sym) { 181 | case SDLK_F11: 182 | return SDLCONSOLE_EVENT_DEBUG_1; 183 | case SDLK_F12: 184 | return SDLCONSOLE_EVENT_DEBUG_2; 185 | default: 186 | if (event.key.keysym.sym == SDLK_LCTRL) sdlconsole_ctrl = 1; 187 | if (event.key.keysym.sym == SDLK_LALT) sdlconsole_alt = 1; 188 | if (sdlconsole_ctrl & sdlconsole_alt) { 189 | sdlconsole_mousegrab(); 190 | } 191 | sdlconsole_curkey = sdlconsole_translateScancode(event.key.keysym.sym); 192 | if (sdlconsole_curkey == 0x00) { 193 | return SDLCONSOLE_EVENT_NONE; 194 | } else { 195 | sdlconsole_lastKey = sdlconsole_curkey; 196 | timing_updateIntervalFreq(sdlconsole_keyTimer, 2); 197 | timing_timerEnable(sdlconsole_keyTimer); 198 | return SDLCONSOLE_EVENT_KEY; 199 | } 200 | } 201 | case SDL_KEYUP: 202 | if (event.key.repeat) return SDLCONSOLE_EVENT_NONE; 203 | if (event.key.keysym.sym == SDLK_LCTRL) sdlconsole_ctrl = 0; 204 | if (event.key.keysym.sym == SDLK_LALT) sdlconsole_alt = 0; 205 | sdlconsole_curkey = sdlconsole_translateScancode(event.key.keysym.sym) | 0x80; 206 | if ((sdlconsole_curkey & 0x7F) == sdlconsole_lastKey) { 207 | timing_timerDisable(sdlconsole_keyTimer); 208 | } 209 | return (sdlconsole_curkey == 0x80) ? SDLCONSOLE_EVENT_NONE : SDLCONSOLE_EVENT_KEY; 210 | case SDL_MOUSEMOTION: 211 | xrel = (event.motion.xrel < -128) ? -128 : (int8_t)event.motion.xrel; 212 | xrel = (event.motion.xrel > 127) ? 127 : (int8_t)event.motion.xrel; 213 | yrel = (event.motion.yrel < -128) ? -128 : (int8_t)event.motion.yrel; 214 | yrel = (event.motion.yrel > 127) ? 127 : (int8_t)event.motion.yrel; 215 | if (sdlconsole_grabbed) { 216 | mouse_action(MOUSE_ACTION_MOVE, MOUSE_NEITHER, xrel, yrel); 217 | } 218 | return SDLCONSOLE_EVENT_NONE; 219 | case SDL_MOUSEBUTTONDOWN: 220 | case SDL_MOUSEBUTTONUP: 221 | if (event.button.button == SDL_BUTTON_LEFT) { 222 | action = MOUSE_ACTION_LEFT; 223 | if (!sdlconsole_grabbed) { 224 | sdlconsole_mousegrab(); 225 | break; 226 | } 227 | } 228 | else if (event.button.button == SDL_BUTTON_RIGHT) { 229 | action = MOUSE_ACTION_RIGHT; 230 | } 231 | if (sdlconsole_grabbed) { 232 | mouse_action(action, (event.button.state == SDL_PRESSED) ? MOUSE_PRESSED : MOUSE_UNPRESSED, 0, 0); 233 | } 234 | return SDLCONSOLE_EVENT_NONE; 235 | case SDL_QUIT: 236 | return SDLCONSOLE_EVENT_QUIT; 237 | } 238 | return SDLCONSOLE_EVENT_NONE; 239 | } 240 | 241 | uint8_t sdlconsole_getScancode() { 242 | //debug_log(DEBUG_DETAIL, "curkey: %02X\r\n", sdlconsole_curkey); 243 | return sdlconsole_curkey; 244 | } 245 | 246 | uint8_t sdlconsole_translateScancode(SDL_Keycode keyval) { 247 | uint8_t i; 248 | for (i = 0; i < 95; i++) { 249 | if (keyval == (SDL_Keycode)sdlconsole_translateMatrix[i][0]) { 250 | return (uint8_t)sdlconsole_translateMatrix[i][1]; 251 | } 252 | } 253 | return 0x00; 254 | } 255 | -------------------------------------------------------------------------------- /XTulator/modules/disk/biosdisk.c: -------------------------------------------------------------------------------- 1 | /* 2 | XTulator: A portable, open-source 80186 PC emulator. 3 | Copyright (C)2020 Mike Chambers 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | /* 21 | 22 | NOTE: I consider HLE of the disk system at the BIOS level a hack. 23 | I want to get rid of this and implement proper FDC and HDC controllers. 24 | This is here for testing purposes of the rest of the system until 25 | I get around to that... 26 | 27 | */ 28 | 29 | #include 30 | #include 31 | #include "biosdisk.h" 32 | #include "../../cpu/cpu.h" 33 | #include "../../debuglog.h" 34 | 35 | DISK_t biosdisk[4]; 36 | uint8_t biosdisk_sectbuf[512]; 37 | 38 | uint8_t bootdrive = 0xFF; 39 | 40 | uint8_t biosdisk_insert(CPU_t* cpu, uint8_t drivenum, char* filename) { 41 | debug_log(DEBUG_INFO, "[BIOSDISK] Inserting disk %u: %s\r\n", drivenum, filename); 42 | if (biosdisk[drivenum].inserted) fclose(biosdisk[drivenum].diskfile); 43 | biosdisk[drivenum].inserted = 1; 44 | biosdisk[drivenum].diskfile = fopen(filename, "r+b"); 45 | if (biosdisk[drivenum].diskfile == NULL) { 46 | biosdisk[drivenum].inserted = 0; 47 | debug_log(DEBUG_INFO, "[BIOSDISK] Failed to insert disk %u: %s\r\n", drivenum, filename); 48 | return 1; 49 | } 50 | fseek(biosdisk[drivenum].diskfile, 0L, SEEK_END); 51 | biosdisk[drivenum].filesize = ftell(biosdisk[drivenum].diskfile); 52 | fseek(biosdisk[drivenum].diskfile, 0L, SEEK_SET); 53 | if (drivenum >= 2) { //it's a hard disk image 54 | biosdisk[drivenum].sects = 63; 55 | biosdisk[drivenum].heads = 16; 56 | biosdisk[drivenum].cyls = biosdisk[drivenum].filesize / (biosdisk[drivenum].sects * biosdisk[drivenum].heads * 512); 57 | cpu_write(cpu, 0x475, biosdisk_gethdcount()); 58 | } 59 | else { //it's a floppy image 60 | biosdisk[drivenum].cyls = 80; 61 | biosdisk[drivenum].sects = 18; 62 | biosdisk[drivenum].heads = 2; 63 | if (biosdisk[drivenum].filesize <= 1228800) biosdisk[drivenum].sects = 15; 64 | if (biosdisk[drivenum].filesize <= 737280) biosdisk[drivenum].sects = 9; 65 | if (biosdisk[drivenum].filesize <= 368640) { 66 | biosdisk[drivenum].cyls = 40; 67 | biosdisk[drivenum].sects = 9; 68 | } 69 | if (biosdisk[drivenum].filesize <= 163840) { 70 | biosdisk[drivenum].cyls = 40; 71 | biosdisk[drivenum].sects = 8; 72 | biosdisk[drivenum].heads = 1; 73 | } 74 | } 75 | return 0; 76 | } 77 | 78 | void biosdisk_eject(CPU_t* cpu, uint8_t drivenum) { 79 | biosdisk[drivenum].inserted = 0; 80 | if (drivenum >= 2) { 81 | cpu_write(cpu, 0x475, biosdisk_gethdcount()); 82 | } 83 | if (biosdisk[drivenum].diskfile != NULL) fclose(biosdisk[drivenum].diskfile); 84 | } 85 | 86 | void biosdisk_read(CPU_t* cpu, uint8_t drivenum, uint16_t dstseg, uint16_t dstoff, uint16_t cyl, uint16_t sect, uint16_t head, uint16_t sectcount) { 87 | uint32_t memdest, lba, fileoffset, cursect, sectoffset; 88 | if (!sect || !biosdisk[drivenum].inserted) return; 89 | lba = ((uint32_t)cyl * (uint32_t)biosdisk[drivenum].heads + (uint32_t)head) * (uint32_t)biosdisk[drivenum].sects + (uint32_t)sect - 1UL; 90 | fileoffset = lba * 512UL; 91 | if (fileoffset > biosdisk[drivenum].filesize) return; 92 | fseek(biosdisk[drivenum].diskfile, fileoffset, SEEK_SET); 93 | memdest = ((uint32_t)dstseg << 4) + (uint32_t)dstoff; 94 | for (cursect = 0; cursect < sectcount; cursect++) { 95 | if (fread(biosdisk_sectbuf, 1, 512, biosdisk[drivenum].diskfile) < 512) break; 96 | for (sectoffset = 0; sectoffset < 512; sectoffset++) { 97 | cpu_write(cpu, memdest++, biosdisk_sectbuf[sectoffset]); 98 | } 99 | } 100 | cpu->regs.byteregs[regal] = cursect; 101 | cpu->cf = 0; 102 | cpu->regs.byteregs[regah] = 0; 103 | } 104 | 105 | void biosdisk_write(CPU_t* cpu, uint8_t drivenum, uint16_t dstseg, uint16_t dstoff, uint16_t cyl, uint16_t sect, uint16_t head, uint16_t sectcount) { 106 | uint32_t memdest, lba, fileoffset, cursect, sectoffset; 107 | if (!sect || !biosdisk[drivenum].inserted) return; 108 | lba = ((uint32_t)cyl * (uint32_t)biosdisk[drivenum].heads + (uint32_t)head) * (uint32_t)biosdisk[drivenum].sects + (uint32_t)sect - 1UL; 109 | fileoffset = lba * 512UL; 110 | if (fileoffset > biosdisk[drivenum].filesize) return; 111 | fseek(biosdisk[drivenum].diskfile, fileoffset, SEEK_SET); 112 | memdest = ((uint32_t)dstseg << 4) + (uint32_t)dstoff; 113 | for (cursect = 0; cursect < sectcount; cursect++) { 114 | for (sectoffset = 0; sectoffset < 512; sectoffset++) { 115 | biosdisk_sectbuf[sectoffset] = cpu_read(cpu, memdest++); 116 | } 117 | fwrite(biosdisk_sectbuf, 1, 512, biosdisk[drivenum].diskfile); 118 | } 119 | cpu->regs.byteregs[regal] = (uint8_t)sectcount; 120 | cpu->cf = 0; 121 | cpu->regs.byteregs[regah] = 0; 122 | } 123 | 124 | void biosdisk_int19h(CPU_t* cpu, uint8_t intnum) { 125 | if (intnum != 0x19) return; 126 | 127 | cpu_write(cpu, 0x475, biosdisk_gethdcount()); 128 | 129 | //put "STI" and then "JMP -1" code at bootloader location in case nothing gets read from disk 130 | cpu_write(cpu, 0x07C00, 0xFB); 131 | cpu_write(cpu, 0x07C01, 0xEB); 132 | cpu_write(cpu, 0x07C02, 0xFE); 133 | 134 | cpu->regs.byteregs[regdl] = bootdrive; 135 | biosdisk_read(cpu, (cpu->regs.byteregs[regdl] & 0x80) ? cpu->regs.byteregs[regdl] - 126 : cpu->regs.byteregs[regdl], 0x0000, 0x7C00, 0, 1, 0, 1); 136 | cpu->segregs[regcs] = 0x0000; 137 | cpu->ip = 0x7C00; 138 | } 139 | 140 | void biosdisk_int13h(CPU_t* cpu, uint8_t intnum) { 141 | static uint8_t lastah = 0, lastcf = 0; 142 | uint8_t curdisk; 143 | 144 | if (intnum != 0x13) return; 145 | 146 | curdisk = cpu->regs.byteregs[regdl]; 147 | if (curdisk & 0x80) curdisk = curdisk - 126; 148 | if (curdisk > 3) { 149 | cpu->cf = 1; 150 | cpu->regs.byteregs[regah] = 1; 151 | return; 152 | } 153 | 154 | switch (cpu->regs.byteregs[regah]) { 155 | case 0: //reset disk system 156 | cpu->regs.byteregs[regah] = 0; 157 | cpu->cf = 0; //useless function in an emulator. say success and return. 158 | break; 159 | case 1: //return last status 160 | cpu->regs.byteregs[regah] = lastah; 161 | cpu->cf = lastcf; 162 | return; 163 | case 2: //read sector(s) into memory 164 | if (biosdisk[curdisk].inserted) { 165 | biosdisk_read(cpu, curdisk, cpu->segregs[reges], getreg16(cpu, regbx), (uint16_t)cpu->regs.byteregs[regch] + ((uint16_t)cpu->regs.byteregs[regcl] / 64) * 256, (uint16_t)cpu->regs.byteregs[regcl] & 63, (uint16_t)cpu->regs.byteregs[regdh], (uint16_t)cpu->regs.byteregs[regal]); 166 | cpu->cf = 0; 167 | cpu->regs.byteregs[regah] = 0; 168 | } 169 | else { 170 | cpu->cf = 1; 171 | cpu->regs.byteregs[regah] = 1; 172 | } 173 | break; 174 | case 3: //write sector(s) from memory 175 | if (biosdisk[curdisk].inserted) { 176 | biosdisk_write(cpu, curdisk, cpu->segregs[reges], getreg16(cpu, regbx), (uint16_t)cpu->regs.byteregs[regch] + ((uint16_t)cpu->regs.byteregs[regcl] / 64) * 256, (uint16_t)cpu->regs.byteregs[regcl] & 63, (uint16_t)cpu->regs.byteregs[regdh], (uint16_t)cpu->regs.byteregs[regal]); 177 | cpu->cf = 0; 178 | cpu->regs.byteregs[regah] = 0; 179 | } 180 | else { 181 | cpu->cf = 1; 182 | cpu->regs.byteregs[regah] = 1; 183 | } 184 | break; 185 | case 4: 186 | case 5: //format track 187 | cpu->cf = 0; 188 | cpu->regs.byteregs[regah] = 0; 189 | break; 190 | case 8: //get drive parameters 191 | if (biosdisk[curdisk].inserted) { 192 | cpu->cf = 0; 193 | cpu->regs.byteregs[regah] = 0; 194 | cpu->regs.byteregs[regch] = biosdisk[curdisk].cyls - 1; 195 | cpu->regs.byteregs[regcl] = biosdisk[curdisk].sects & 63; 196 | cpu->regs.byteregs[regcl] = cpu->regs.byteregs[regcl] + (biosdisk[curdisk].cyls / 256) * 64; 197 | cpu->regs.byteregs[regdh] = biosdisk[curdisk].heads - 1; 198 | if (curdisk < 2) { 199 | cpu->regs.byteregs[regbl] = 4; //else regs.byteregs[regbl] = 0; 200 | cpu->regs.byteregs[regdl] = 2; 201 | } 202 | else cpu->regs.byteregs[regdl] = biosdisk_gethdcount(); 203 | } 204 | else { 205 | cpu->cf = 1; 206 | cpu->regs.byteregs[regah] = 0xAA; 207 | } 208 | break; 209 | default: 210 | cpu->cf = 1; 211 | } 212 | lastah = cpu->regs.byteregs[regah]; 213 | lastcf = cpu->cf; 214 | if (cpu->regs.byteregs[regdl] & 0x80) cpu_write(cpu, 0x474, cpu->regs.byteregs[regah]); 215 | } 216 | 217 | uint8_t biosdisk_gethdcount() { 218 | uint8_t ret = 0, i; 219 | 220 | for (i = 2; i < 4; i++) { 221 | if (biosdisk[i].inserted) ret++; 222 | } 223 | return ret; 224 | } 225 | 226 | void biosdisk_init(CPU_t* cpu) { 227 | cpu_registerIntCallback(cpu, 0x13, biosdisk_int13h); 228 | cpu_registerIntCallback(cpu, 0x19, biosdisk_int19h); 229 | } 230 | -------------------------------------------------------------------------------- /XTulator/modules/audio/blaster.c: -------------------------------------------------------------------------------- 1 | /* 2 | XTulator: A portable, open-source 80186 PC emulator. 3 | Copyright (C)2020 Mike Chambers 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | /* 21 | Emulation of the Sound Blaster 2.0 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include "../../config.h" 28 | #include "../../debuglog.h" 29 | #include "../../ports.h" 30 | #include "../../timing.h" 31 | #include "../../chipset/i8237.h" 32 | #include "../../chipset/i8259.h" 33 | #include "blaster.h" 34 | 35 | const int16_t cmd_E2_table[9] = { 0x01, -0x02, -0x04, 0x08, -0x10, 0x20, 0x40, -0x80, -106 }; 36 | 37 | void blaster_putreadbuf(BLASTER_t* blaster, uint8_t value) { 38 | if (blaster->readlen == 16) return; 39 | 40 | blaster->readbuf[blaster->readlen++] = value; 41 | } 42 | 43 | uint8_t blaster_getreadbuf(BLASTER_t* blaster) { 44 | uint8_t ret; 45 | 46 | ret = blaster->readbuf[0]; 47 | if (blaster->readlen > 0) { 48 | blaster->readlen--; 49 | } 50 | memmove(blaster->readbuf, blaster->readbuf + 1, 15); 51 | 52 | return ret; 53 | } 54 | 55 | void blaster_reset(BLASTER_t* blaster) { 56 | blaster->dspenable = 0; 57 | blaster->sample = 0; 58 | blaster->readlen = 0; 59 | blaster_putreadbuf(blaster, 0xAA); 60 | } 61 | 62 | void blaster_writecmd(BLASTER_t* blaster, uint8_t value) { 63 | switch (blaster->lastcmd) { 64 | case 0x10: //direct DAC, 8-bit 65 | blaster->sample = value; 66 | blaster->sample -= 128; 67 | blaster->sample *= 256; 68 | blaster->lastcmd = 0; 69 | return; 70 | case 0x14: //DMA DAC, 8-bit 71 | case 0x24: 72 | if (blaster->writehilo == 0) { 73 | blaster->dmalen = value; 74 | blaster->writehilo = 1; 75 | } else { 76 | blaster->dmalen |= (uint32_t)value << 8; 77 | blaster->dmalen++; 78 | blaster->lastcmd = 0; 79 | blaster->dmacount = 0; 80 | blaster->silencedsp = 0; 81 | blaster->autoinit = 0; 82 | blaster->dorecord = (blaster->lastcmd == 0x24) ? 1 : 0; 83 | blaster->activedma = 1; 84 | timing_timerEnable(blaster->timer); 85 | #ifdef DEBUG_BLASTER 86 | debug_log(DEBUG_DETAIL, "[BLASTER] Begin DMA transfer mode with %lu byte blocks\r\n", blaster->dmalen); 87 | #endif 88 | } 89 | return; 90 | case 0x40: //set time constant 91 | blaster->timeconst = value; 92 | blaster->samplerate = 1000000.0 / (256.0 - (double)value); 93 | timing_updateIntervalFreq(blaster->timer, blaster->samplerate); 94 | blaster->lastcmd = 0; 95 | #ifdef DEBUG_BLASTER 96 | debug_log(DEBUG_DETAIL, "[BLASTER] Set time constant: %u (Sample rate: %f Hz)\r\n", value, blaster->samplerate); 97 | #endif 98 | return; 99 | case 0x48: //set DMA block size 100 | if (blaster->writehilo == 0) { 101 | blaster->dmalen = value; 102 | blaster->writehilo = 1; 103 | } else { 104 | blaster->dmalen |= (uint32_t)value << 8; 105 | blaster->dmalen++; 106 | blaster->lastcmd = 0; 107 | } 108 | return; 109 | case 0x80: //silence DAC 110 | if (blaster->writehilo == 0) { 111 | blaster->dmalen = value; 112 | blaster->writehilo = 1; 113 | } else { 114 | blaster->dmalen |= (uint32_t)value << 8; 115 | blaster->dmalen++; 116 | blaster->lastcmd = 0; 117 | blaster->dmacount = 0; 118 | blaster->silencedsp = 1; 119 | blaster->autoinit = 0; 120 | timing_timerEnable(blaster->timer); 121 | } 122 | return; 123 | case 0xE0: //DSP identification (returns bitwise NOT of data byte) 124 | blaster_putreadbuf(blaster, ~value); 125 | blaster->lastcmd = 0; 126 | return; 127 | case 0xE2: //DMA identification write 128 | { 129 | int16_t val = 0xAA, i; 130 | for (i = 0; i < 8; i++) { 131 | if ((value >> i) & 0x01) { 132 | val += cmd_E2_table[i]; 133 | } 134 | } 135 | val += cmd_E2_table[8]; 136 | i8237_write(blaster->i8237, blaster->dmachan, (uint8_t)val); 137 | blaster->lastcmd = 0; 138 | return; 139 | } 140 | case 0xE4: //write test register 141 | blaster->testreg = value; 142 | blaster->lastcmd = 0; 143 | return; 144 | } 145 | 146 | switch (value) { 147 | case 0x10: //direct DAC, 8-bit 148 | break; 149 | case 0x14: //DMA DAC, 8-bit 150 | case 0x24: 151 | blaster->writehilo = 0; 152 | break; 153 | case 0x1C: //auto-initialize DMA DAC, 8-bit 154 | case 0x2C: 155 | blaster->dmacount = 0; 156 | blaster->silencedsp = 0; 157 | blaster->autoinit = 1; 158 | blaster->dorecord = (value == 0x2C) ? 1 : 0; 159 | blaster->activedma = 1; 160 | timing_timerEnable(blaster->timer); 161 | #ifdef DEBUG_BLASTER 162 | debug_log(DEBUG_DETAIL, "[BLASTER] Begin auto-init DMA transfer mode with %lu byte blocks\r\n", blaster->dmalen); 163 | #endif 164 | break; 165 | case 0x20: //direct DAC, 8-bit record 166 | blaster_putreadbuf(blaster, 128); //Silence, though I might add actual recording support later. 167 | break; 168 | case 0x40: //set time constant 169 | break; 170 | case 0x48: //set DMA block size 171 | blaster->writehilo = 0; 172 | break; 173 | case 0x80: //silence DAC 174 | blaster->writehilo = 0; 175 | break; 176 | case 0xD0: //halt DMA operation, 8-bit 177 | blaster->activedma = 0; 178 | timing_timerDisable(blaster->timer); 179 | break; 180 | case 0xD1: //speaker on 181 | blaster->dspenable = 1; 182 | break; 183 | case 0xD3: //speaker off 184 | blaster->dspenable = 0; 185 | break; 186 | case 0xD4: //continue DMA operation, 8-bit 187 | blaster->activedma = 1; 188 | timing_timerEnable(blaster->timer); 189 | break; 190 | case 0xDA: //exit auto-initialize DMA operation, 8-bit 191 | blaster->activedma = 0; 192 | blaster->autoinit = 0; 193 | break; 194 | case 0xE0: //DSP identification (returns bitwise NOT of data byte) 195 | break; 196 | case 0xE1: //DSP version (SB 2.0 is DSP 2.01) 197 | blaster_putreadbuf(blaster, 2); 198 | blaster_putreadbuf(blaster, 1); 199 | break; 200 | case 0xE2: //DMA identification write 201 | break; 202 | case 0xE4: //write test register 203 | break; 204 | case 0xE8: //read test register 205 | blaster_putreadbuf(blaster, blaster->testreg); 206 | break; 207 | case 0xF2: //trigger 8-bit IRQ 208 | i8259_doirq(blaster->i8259, blaster->irq); 209 | break; 210 | case 0xF8: //Undocumented 211 | blaster_putreadbuf(blaster, 0); 212 | break; 213 | default: 214 | debug_log(DEBUG_ERROR, "[BLASTER] Unrecognized command: 0x%02X\r\n", value); 215 | } 216 | 217 | blaster->lastcmd = value; 218 | } 219 | 220 | void blaster_write(BLASTER_t* blaster, uint16_t addr, uint8_t value) { 221 | #ifdef DEBUG_BLASTER 222 | debug_log(DEBUG_DETAIL, "[BLASTER] Write %03X: %02X\r\n", addr, value); 223 | #endif 224 | addr &= 0x0F; 225 | 226 | switch (addr) { 227 | case 0x06: 228 | if (value == 0) { 229 | blaster_reset(blaster); 230 | } 231 | break; 232 | case 0x0C: //DSP write (command/data) 233 | blaster_writecmd(blaster, value); 234 | break; 235 | } 236 | } 237 | 238 | uint8_t blaster_read(BLASTER_t* blaster, uint16_t addr) { 239 | uint8_t ret = 0xFF; 240 | 241 | #ifdef DEBUG_BLASTER 242 | debug_log(DEBUG_DETAIL, "[BLASTER] Read %03X\r\n", addr); 243 | #endif 244 | addr &= 0x0F; 245 | 246 | switch (addr) { 247 | case 0x0A: 248 | return blaster_getreadbuf(blaster); 249 | case 0x0C: 250 | return 0x00; 251 | case 0x0E: 252 | return (blaster->readlen > 0) ? 0x80 : 0x00; 253 | } 254 | 255 | return ret; 256 | } 257 | 258 | void blaster_generateSample(BLASTER_t* blaster) { //for DMA mode 259 | if (blaster->silencedsp == 0) { 260 | if (blaster->dorecord == 0) { 261 | blaster->sample = i8237_read(blaster->i8237, blaster->dmachan); 262 | blaster->sample -= 128; 263 | blaster->sample *= 256; 264 | } else { 265 | i8237_write(blaster->i8237, blaster->dmachan, 128); //silence 266 | } 267 | } else { 268 | blaster->sample = 0; 269 | } 270 | 271 | if (++blaster->dmacount == blaster->dmalen) { 272 | blaster->dmacount = 0; 273 | i8259_doirq(blaster->i8259, blaster->irq); 274 | if (blaster->autoinit == 0) { 275 | blaster->activedma = 0; 276 | timing_timerDisable(blaster->timer); 277 | } 278 | } 279 | 280 | if (blaster->dspenable == 0) { 281 | blaster->sample = 0; 282 | return; 283 | } 284 | } 285 | 286 | int16_t blaster_getSample(BLASTER_t* blaster) { 287 | return blaster->sample; 288 | } 289 | 290 | void blaster_init(BLASTER_t* blaster, I8237_t* i8237, I8259_t* i8259, uint16_t base, uint8_t dma, uint8_t irq) { 291 | debug_log(DEBUG_INFO, "[BLASTER] Initializing Sound Blaster 2.0 at base port 0x%03X, IRQ %u, DMA %u\r\n", base, irq, dma); 292 | memset(blaster, 0, sizeof(BLASTER_t)); 293 | blaster->i8237 = i8237; 294 | blaster->i8259 = i8259; 295 | blaster->dmachan = dma; 296 | blaster->irq = irq; 297 | ports_cbRegister(base, 16, (void*)blaster_read, NULL, (void*)blaster_write, NULL, blaster); 298 | 299 | //TODO: error handling 300 | blaster->timer = timing_addTimer(blaster_generateSample, blaster, 22050, TIMING_DISABLED); 301 | } 302 | -------------------------------------------------------------------------------- /XTulator/XTulator.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {bf103401-0663-4f12-95ea-cace8ecb9897} 18 | 19 | 20 | {fdadc56d-c42e-4c8d-a293-712a0247b954} 21 | 22 | 23 | {efc1f185-55dd-4e54-a58e-a47a289b5bac} 24 | 25 | 26 | {8efeb712-905f-42bb-af79-9c072a20f93b} 27 | 28 | 29 | {9c9ad04c-d2cd-401b-8b91-66902129fba8} 30 | 31 | 32 | {f2f0e491-b554-4d7e-b6e4-123d9803ee99} 33 | 34 | 35 | {a44f6679-836a-4b0a-8a89-ef00e3a73976} 36 | 37 | 38 | {b097e4e1-b99b-45bf-a278-034c4466ce4c} 39 | 40 | 41 | {ea658897-8f43-4848-95f0-c4c0f592d1b3} 42 | 43 | 44 | {79052ac5-df15-48d8-b15b-55b2e3204608} 45 | 46 | 47 | {e85a1a52-dfab-4f66-b0c0-f0cdf3aa2606} 48 | 49 | 50 | {110f8868-fe40-4b0a-819c-055b67af4468} 51 | 52 | 53 | {2ef550bc-0467-4093-b106-23e553cf64c9} 54 | 55 | 56 | {86a0dc4c-5e1a-4b4a-81d8-633922dd2bc7} 57 | 58 | 59 | {f2b9ce14-f016-48ea-aa83-dd4e3836322a} 60 | 61 | 62 | {de0d3374-1dea-4ac7-a9a1-5b8cb4758adb} 63 | 64 | 65 | 66 | 67 | Source Files\cpu 68 | 69 | 70 | Source Files 71 | 72 | 73 | Source Files\chipset 74 | 75 | 76 | Source Files\chipset 77 | 78 | 79 | Source Files\modules\disk 80 | 81 | 82 | Source Files\modules\video 83 | 84 | 85 | Source Files\modules\video 86 | 87 | 88 | Source Files 89 | 90 | 91 | Source Files 92 | 93 | 94 | Source Files 95 | 96 | 97 | Source Files 98 | 99 | 100 | Source Files\chipset 101 | 102 | 103 | Source Files\chipset 104 | 105 | 106 | Source Files\modules\video 107 | 108 | 109 | Source Files\modules\audio 110 | 111 | 112 | Source Files\modules\audio 113 | 114 | 115 | Source Files 116 | 117 | 118 | Source Files 119 | 120 | 121 | Source Files\modules\audio 122 | 123 | 124 | Source Files\modules\audio 125 | 126 | 127 | Source Files\chipset 128 | 129 | 130 | Source Files\modules\input 131 | 132 | 133 | Source Files\modules\io 134 | 135 | 136 | Source Files 137 | 138 | 139 | Source Files 140 | 141 | 142 | Source Files\modules\disk 143 | 144 | 145 | Source Files\modules\audio 146 | 147 | 148 | Source Files\modules\io 149 | 150 | 151 | Source Files\modules\io 152 | 153 | 154 | Source Files 155 | 156 | 157 | 158 | 159 | Header Files\cpu 160 | 161 | 162 | Header Files\cpu 163 | 164 | 165 | Header Files\chipset 166 | 167 | 168 | Header Files\chipset 169 | 170 | 171 | Header Files\modules\disk 172 | 173 | 174 | Header Files\modules\input 175 | 176 | 177 | Header Files\modules\video 178 | 179 | 180 | Header Files\modules\video 181 | 182 | 183 | Header Files 184 | 185 | 186 | Header Files 187 | 188 | 189 | Header Files 190 | 191 | 192 | Header Files\modules\input 193 | 194 | 195 | Header Files\chipset 196 | 197 | 198 | Header Files\chipset 199 | 200 | 201 | Header Files\modules\video 202 | 203 | 204 | Header Files\modules\audio 205 | 206 | 207 | Header Files\modules\audio 208 | 209 | 210 | Header Files 211 | 212 | 213 | Header Files 214 | 215 | 216 | Header Files 217 | 218 | 219 | Header Files\modules\audio 220 | 221 | 222 | Header Files\modules\audio 223 | 224 | 225 | Header Files\chipset 226 | 227 | 228 | Header Files\modules\io 229 | 230 | 231 | Header Files 232 | 233 | 234 | Header Files\modules\input 235 | 236 | 237 | Header Files 238 | 239 | 240 | Header Files\modules\disk 241 | 242 | 243 | Header Files 244 | 245 | 246 | Header Files\modules\audio 247 | 248 | 249 | Header Files\modules\io 250 | 251 | 252 | Header Files\modules\io 253 | 254 | 255 | Header Files\modules\io 256 | 257 | 258 | Header Files 259 | 260 | 261 | -------------------------------------------------------------------------------- /XTulator/modules/video/cga.c: -------------------------------------------------------------------------------- 1 | /* 2 | XTulator: A portable, open-source 80186 PC emulator. 3 | Copyright (C)2020 Mike Chambers 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #ifdef _WIN32 24 | #include 25 | #else 26 | #include 27 | pthread_t cga_renderThreadID; 28 | #endif 29 | #include "cga.h" 30 | #include "../../config.h" 31 | #include "../../timing.h" 32 | #include "../../utility.h" 33 | #include "../../ports.h" 34 | #include "../../memory.h" 35 | #include "sdlconsole.h" 36 | #include "../../debuglog.h" 37 | 38 | const uint8_t cga_palette[16][3] = { //R, G, B 39 | { 0x00, 0x00, 0x00 }, //black 40 | { 0x00, 0x00, 0xAA }, //blue 41 | { 0x00, 0xAA, 0x00 }, //green 42 | { 0x00, 0xAA, 0xAA }, //cyan 43 | { 0xAA, 0x00, 0x00 }, //red 44 | { 0xAA, 0x00, 0xAA }, //magenta 45 | { 0xAA, 0x55, 0x00 }, //brown 46 | { 0xAA, 0xAA, 0xAA }, //light gray 47 | { 0x55, 0x55, 0x55 }, //dark gray 48 | { 0x55, 0x55, 0xFF }, //light blue 49 | { 0x55, 0xFF, 0x55 }, //light green 50 | { 0x55, 0xFF, 0xFF }, //light cyan 51 | { 0xFF, 0x55, 0x55 }, //light red 52 | { 0xFF, 0x55, 0xFF }, //light magenta 53 | { 0xFF, 0xFF, 0x55 }, //yellow 54 | { 0xFF, 0xFF, 0xFF } //white 55 | }; 56 | 57 | const uint8_t cga_gfxpal[2][2][4] = { //palettes for 320x200 graphics mode 58 | { 59 | { 0, 2, 4, 6 }, //normal palettes 60 | { 0, 3, 5, 7 } 61 | }, 62 | { 63 | { 0, 10, 12, 14 }, //intense palettes 64 | { 0, 11, 13, 15 } 65 | } 66 | }; 67 | 68 | uint8_t cga_font[4096]; 69 | uint32_t cga_framebuffer[400][640]; 70 | uint16_t cga_cursorloc = 0; 71 | uint8_t cga_indexreg = 0, cga_datareg[256], cga_regs[16]; 72 | uint8_t cga_cursor_blink_state = 0; 73 | uint8_t *cga_RAM = NULL; 74 | 75 | volatile uint8_t cga_doDraw = 1; 76 | 77 | int cga_init() { 78 | int x, y; 79 | 80 | debug_log(DEBUG_INFO, "[CGA] Initializing CGA video device\r\n"); 81 | 82 | if (utility_loadFile(cga_font, 4096, "roms/video/cgachar.bin")) { 83 | debug_log(DEBUG_ERROR, "[CGA] Failed to load character generator ROM\r\n"); 84 | return -1; 85 | } 86 | 87 | for (y = 0; y < 400; y++) { 88 | for (x = 0; x < 640; x++) { 89 | cga_framebuffer[y][x] = cga_color(CGA_BLACK); 90 | } 91 | } 92 | sdlconsole_blit((uint32_t *)cga_framebuffer, 640, 400, 640 * sizeof(uint32_t)); 93 | 94 | timing_addTimer(cga_blinkCallback, NULL, 3, TIMING_ENABLED); 95 | timing_addTimer(cga_scanlineCallback, NULL, 62800, TIMING_ENABLED); 96 | timing_addTimer(cga_drawCallback, NULL, 60, TIMING_ENABLED); 97 | /* 98 | NOTE: CGA scanlines are clocked at 15.7 KHz. We are breaking each scanline into 99 | four parts and using the last part as a very approximate horizontal retrace period. 100 | 101 | 15700 x 4 = 62800 102 | 103 | See cga_scanlineCallback function for more details. 104 | */ 105 | 106 | cga_RAM = (uint8_t*)malloc(16384); 107 | if (cga_RAM == NULL) { 108 | debug_log(DEBUG_ERROR, "[CGA] Failed to allocate video memory\r\n"); 109 | return -1; 110 | } 111 | 112 | //TODO: error checking below 113 | #ifdef _WIN32 114 | _beginthread(cga_renderThread, 0, NULL); 115 | #else 116 | pthread_create(&cga_renderThreadID, NULL, cga_renderThread, NULL); 117 | #endif 118 | 119 | ports_cbRegister(0x3D0, 16, (void*)cga_readport, NULL, (void*)cga_writeport, NULL, NULL); 120 | memory_mapCallbackRegister(0xB8000, 0x4000, (void*)cga_readmemory, (void*)cga_writememory, NULL); 121 | 122 | return 0; 123 | } 124 | 125 | void cga_update(uint32_t start_x, uint32_t start_y, uint32_t end_x, uint32_t end_y) { 126 | uint32_t addr, startaddr, cursorloc, cursor_x, cursor_y; 127 | uint32_t scx, scy, x, y; 128 | uint8_t cc, attr, fontdata, blink, mode, colorset, intensity, blinkenable; 129 | 130 | if (cga_regs[0x8] & 0x02) { //graphics modes 131 | mode = (cga_regs[0x8] & 0x10) ? CGA_MODE_GRAPHICS_HI : CGA_MODE_GRAPHICS_LO; 132 | intensity = (cga_regs[0x9] & 0x10) ? 1 : 0; 133 | colorset = (cga_regs[0x9] & 0x20) ? 1 : 0; 134 | } else { //text modes 135 | mode = (cga_regs[0x8] & 0x01) ? CGA_MODE_TEXT_80X25 : CGA_MODE_TEXT_40X25; 136 | blinkenable = (cga_regs[0x8] & 0x20) ? 1 : 0; 137 | } 138 | startaddr = (((uint32_t)cga_datareg[0x12] & 0x3F) << 8) | (uint32_t)cga_datareg[0x13]; 139 | cursorloc = (((uint32_t)cga_datareg[0xE] << 8) & 0xFF00) | (uint32_t)cga_datareg[0xF]; 140 | 141 | switch (mode) { 142 | case CGA_MODE_TEXT_80X25: 143 | cursor_x = cursorloc % 80; 144 | cursor_y = cursorloc / 80; 145 | for (scy = start_y; scy <= end_y; scy++) { 146 | y = scy / (((cga_datareg[0x09] & 0x1F) + 1) * 2); 147 | for (scx = start_x; scx <= end_x; scx++) { 148 | x = scx / 8; 149 | addr = startaddr + ((y * 80) + x) * 2; 150 | cc = cga_RAM[addr]; 151 | attr = cga_RAM[addr + 1]; 152 | blink = attr >> 7; 153 | if (blinkenable) attr &= 0x7F; //enabling text mode blink attribute limits background color selection 154 | fontdata = cga_font[2048 + (cc * 8) + ((scy % 16) / 2)]; 155 | fontdata = (fontdata >> (7 - (scx % 8))) & 1; 156 | if ((y == cursor_y) && (x == cursor_x) && 157 | ((uint8_t)(scy % 16) >= (cga_datareg[CGA_REG_DATA_CURSOR_BEGIN] & 31) * 2) && 158 | ((uint8_t)(scy % 16) <= (cga_datareg[CGA_REG_DATA_CURSOR_END] & 31) * 2) && 159 | cga_cursor_blink_state && blinkenable) { //cursor should be displayed 160 | cga_framebuffer[scy][scx] = cga_color(attr & 0x0F); 161 | } 162 | else { 163 | if (blinkenable && blink && !cga_cursor_blink_state) { 164 | fontdata = 0; //all pixels in character get background color if blink attribute set and blink visible state is false 165 | } 166 | cga_framebuffer[scy][scx] = cga_color(fontdata ? (attr & 0x0F) : (attr >> 4)); 167 | } 168 | } 169 | } 170 | break; 171 | case CGA_MODE_TEXT_40X25: 172 | cursor_x = cursorloc % 40; 173 | cursor_y = cursorloc / 40; 174 | for (scy = start_y; scy <= end_y; scy++) { 175 | y = scy / 16; 176 | for (scx = start_x; scx <= end_x; scx += 2) { 177 | x = scx / 16; 178 | addr = startaddr + ((y * 40) + x) * 2; 179 | cc = cga_RAM[addr]; 180 | attr = cga_RAM[addr + 1]; 181 | blink = attr >> 7; 182 | if (blinkenable) attr &= 0x7F; //enabling text mode blink attribute limits background color selection 183 | fontdata = cga_font[2048 + (cc * 8) + ((scy % 16) / 2)]; 184 | fontdata = (fontdata >> (7 - ((scx / 2) % 8))) & 1; 185 | if ((y == cursor_y) && (x == cursor_x) && 186 | ((uint8_t)(scy % 16) >= (cga_datareg[CGA_REG_DATA_CURSOR_BEGIN] & 31) * 2) && 187 | ((uint8_t)(scy % 16) <= (cga_datareg[CGA_REG_DATA_CURSOR_END] & 31) * 2) && 188 | cga_cursor_blink_state && blinkenable) { 189 | cga_framebuffer[scy][scx] = cga_color(attr & 0x0F); 190 | } 191 | else { 192 | if (blinkenable && blink && !cga_cursor_blink_state) { 193 | fontdata = 0; 194 | } 195 | cga_framebuffer[scy][scx] = cga_color(fontdata ? (attr & 0x0F) : (attr >> 4)); 196 | } 197 | cga_framebuffer[scy][scx + 1] = cga_framebuffer[scy][scx]; //double pixels horizontally 198 | } 199 | } 200 | break; 201 | case CGA_MODE_GRAPHICS_LO: 202 | for (scy = start_y; scy <= end_y; scy += 2) { 203 | uint8_t isodd; 204 | isodd = scy & 2; 205 | y = scy >> 2; 206 | for (scx = start_x; scx <= end_x; scx += 2) { 207 | x = scx >> 1; 208 | addr = (isodd ? 0x2000 : 0x0000) + (y * 80) + (x >> 2); 209 | cc = cga_RAM[addr]; 210 | cc = cga_gfxpal[intensity][colorset][(cc >> ((3 - (x & 3)) << 1)) & 3]; 211 | cga_framebuffer[scy][scx] = cga_color(cc); 212 | cga_framebuffer[scy][scx + 1] = cga_framebuffer[scy][scx]; 213 | cga_framebuffer[scy + 1][scx + 1] = cga_framebuffer[scy][scx]; 214 | cga_framebuffer[scy + 1][scx] = cga_framebuffer[scy][scx]; 215 | } 216 | } 217 | break; 218 | case CGA_MODE_GRAPHICS_HI: 219 | cursor_x = cursorloc % 40; 220 | cursor_y = cursorloc / 40; 221 | for (scy = start_y; scy <= end_y; scy += 2) { 222 | uint8_t isodd; 223 | isodd = scy & 2; 224 | y = scy >> 2; 225 | for (scx = start_x; scx <= end_x; scx++) { 226 | x = scx; 227 | addr = (isodd ? 0x2000 : 0x0000) + (y * 80) + (x >> 3); 228 | cc = cga_RAM[addr]; 229 | cc = ((cc >> (7 - (x & 7))) & 1) * 15; 230 | cga_framebuffer[scy][scx] = cga_color(cc); 231 | cga_framebuffer[scy + 1][scx] = cga_framebuffer[scy][scx]; 232 | } 233 | } 234 | break; 235 | } 236 | 237 | sdlconsole_blit((uint32_t *)cga_framebuffer, 640, 400, 640 * sizeof(uint32_t)); 238 | } 239 | 240 | void cga_renderThread(void* dummy) { 241 | while (running) { 242 | if (cga_doDraw == 1) { 243 | cga_update(0, 0, 639, 399); 244 | cga_doDraw = 0; 245 | } 246 | else { 247 | utility_sleep(1); 248 | } 249 | } 250 | #ifdef _WIN32 251 | _endthread(); 252 | #else 253 | pthread_exit(NULL); 254 | #endif 255 | } 256 | 257 | void cga_writeport(void* dummy, uint16_t port, uint8_t value) { 258 | #ifdef DEBUG_CGA 259 | debug_log(DEBUG_DETAIL, "Write CGA port: %02X -> %03X (indexreg = %02X)\r\n", value, port, cga_indexreg); 260 | #endif 261 | switch (port) { 262 | case 0x3D4: 263 | cga_indexreg = value; 264 | break; 265 | case 0x3D5: 266 | cga_datareg[cga_indexreg] = value; 267 | break; 268 | case 0x3DA: 269 | break; 270 | default: 271 | cga_regs[port - 0x3D0] = value; 272 | } 273 | } 274 | 275 | uint8_t cga_readport(void* dummy, uint16_t port) { 276 | #ifdef DEBUG_CGA 277 | debug_log(DEBUG_DETAIL, "Read CGA port: %03X (indexreg = %02X)\r\n", port, cga_indexreg); 278 | #endif 279 | switch (port) { 280 | case 0x3D4: 281 | return cga_indexreg; 282 | case 0x3D5: 283 | //if ((cga_indexreg < 0x0E) || (cga_indexreg > 0x0F)) return 0xFF; 284 | return cga_datareg[cga_indexreg]; 285 | case 0x3DA: 286 | return cga_regs[0xA]; //rand() & 0xF; 287 | } 288 | return cga_regs[port - 0x3D0]; //0xFF; 289 | } 290 | 291 | void cga_writememory(void* dummy, uint32_t addr, uint8_t value) { 292 | addr -= 0xB8000; 293 | if (addr >= 16384) return; 294 | 295 | cga_RAM[addr] = value; 296 | } 297 | 298 | uint8_t cga_readmemory(void* dummy, uint32_t addr) { 299 | addr -= 0xB8000; 300 | if (addr >= 16384) return 0xFF; 301 | 302 | return cga_RAM[addr]; 303 | } 304 | 305 | void cga_blinkCallback(void* dummy) { 306 | cga_cursor_blink_state ^= 1; 307 | } 308 | 309 | void cga_scanlineCallback(void* dummy) { 310 | /* 311 | NOTE: We are only doing very approximate CGA timing. Breaking the horizontal scan into 312 | four parts and setting the display inactive bit on 3DAh on the last quarter of it. Being 313 | more precise shouldn't be necessary and will take much more host CPU time. 314 | 315 | TODO: Look into whether this is true? So far, things are working fine. 316 | */ 317 | static uint16_t scanline = 0, hpart = 0; 318 | 319 | cga_regs[0xA] = 6; //light pen bits always high 320 | cga_regs[0xA] |= (hpart == 3) ? 1 : 0; 321 | cga_regs[0xA] |= (scanline >= 224) ? 8 : 0; 322 | 323 | hpart++; 324 | if (hpart == 4) { 325 | /*if (scanline < 200) { 326 | cga_update(0, (scanline<<1), 639, (scanline<<1)+1); 327 | }*/ 328 | hpart = 0; 329 | scanline++; 330 | } 331 | if (scanline == 256) { 332 | scanline = 0; 333 | } 334 | } 335 | 336 | void cga_drawCallback(void* dummy) { 337 | cga_doDraw = 1; 338 | } 339 | -------------------------------------------------------------------------------- /XTulator/machine.c: -------------------------------------------------------------------------------- 1 | /* 2 | XTulator: A portable, open-source 80186 PC emulator. 3 | Copyright (C)2020 Mike Chambers 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License 7 | as published by the Free Software Foundation; either version 2 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 | */ 19 | 20 | /* 21 | Machine definitions. 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include "config.h" 29 | #include "debuglog.h" 30 | #include "cpu/cpu.h" 31 | #include "chipset/i8259.h" 32 | #include "chipset/i8253.h" 33 | #include "chipset/i8237.h" 34 | #include "chipset/i8255.h" 35 | #include "chipset/uart.h" 36 | #include "modules/audio/pcspeaker.h" 37 | #include "modules/audio/opl2.h" 38 | #include "modules/audio/blaster.h" 39 | #include "modules/disk/biosdisk.h" 40 | #include "modules/disk/fdc.h" 41 | #include "modules/input/mouse.h" 42 | #include "modules/input/input.h" 43 | #ifdef USE_NE2000 44 | #include "modules/io/ne2000.h" 45 | #include "modules/io/pcap-win32.h" 46 | #endif 47 | #include "modules/io/tcpmodem.h" 48 | #include "modules/video/cga.h" 49 | #include "modules/video/vga.h" 50 | #include "rtc.h" 51 | #include "memory.h" 52 | #include "utility.h" 53 | #include "timing.h" 54 | #include "machine.h" 55 | 56 | /* 57 | ID string, full description, init function, default video, speed in MHz (-1 = unlimited), default hardware flags 58 | */ 59 | const MACHINEDEF_t machine_defs[] = { 60 | { "generic_xt", "Generic XT clone with VGA, speed unlimited", machine_init_generic_xt, VIDEO_CARD_VGA, -1, MACHINE_HW_BLASTER | MACHINE_HW_UART1_MOUSE | MACHINE_HW_DISK_HLE | MACHINE_HW_RTC }, 61 | { "ibm_xt", "IBM XT", machine_init_generic_xt, VIDEO_CARD_CGA, 4.77, MACHINE_HW_UART1_MOUSE | MACHINE_HW_RTC }, 62 | { "ami_xt", "AMI XT clone", machine_init_generic_xt, VIDEO_CARD_CGA, 4.77, MACHINE_HW_UART1_MOUSE | MACHINE_HW_RTC }, 63 | { "phoenix_xt", "Pheonix XT clone", machine_init_generic_xt, VIDEO_CARD_CGA, 4.77, MACHINE_HW_UART1_MOUSE | MACHINE_HW_RTC }, 64 | { "xi8088", "Xi 8088", machine_init_generic_xt, VIDEO_CARD_CGA, 4.77, MACHINE_HW_UART1_MOUSE | MACHINE_HW_RTC }, 65 | { "zenithss", "Zenith SuperSport 8088", machine_init_generic_xt, VIDEO_CARD_CGA, 4.77, MACHINE_HW_UART1_MOUSE | MACHINE_HW_RTC }, 66 | { "landmark", "Supersoft/Landmark diagnostic ROM", machine_init_generic_xt, VIDEO_CARD_CGA, 4.77, MACHINE_HW_UART1_MOUSE | MACHINE_HW_RTC }, 67 | { NULL } 68 | }; 69 | 70 | const MACHINEMEM_t machine_mem[][10] = { 71 | //Generic XT clone 72 | { 73 | { MACHINE_MEM_RAM, 0x00000, 0xA0000, MACHINE_ROM_ISNOTROM, NULL }, 74 | #ifndef USE_DISK_HLE 75 | { MACHINE_MEM_ROM, 0xD0000, 0x02000, MACHINE_ROM_REQUIRED, "roms/disk/ide_xt.bin" }, 76 | #endif 77 | { MACHINE_MEM_ROM, 0xFE000, 0x02000, MACHINE_ROM_REQUIRED, "roms/machine/generic_xt/pcxtbios.bin" }, 78 | { MACHINE_MEM_ENDLIST, 0, 0, 0, NULL } 79 | }, 80 | 81 | //IBM XT 82 | { 83 | { MACHINE_MEM_RAM, 0x00000, 0xA0000, MACHINE_ROM_ISNOTROM, NULL }, 84 | #ifndef USE_DISK_HLE 85 | { MACHINE_MEM_ROM, 0xD0000, 0x02000, MACHINE_ROM_REQUIRED, "roms/disk/ide_xt.bin" }, 86 | #endif 87 | { MACHINE_MEM_ROM, 0xF0000, 0x08000, MACHINE_ROM_REQUIRED, "roms/machine/ibm_xt/5000027.u19" }, 88 | { MACHINE_MEM_ROM, 0xF8000, 0x08000, MACHINE_ROM_REQUIRED, "roms/machine/ibm_xt/1501512.u18" }, 89 | { MACHINE_MEM_ENDLIST, 0, 0, 0, NULL } 90 | }, 91 | 92 | //AMI XT clone 93 | { 94 | { MACHINE_MEM_RAM, 0x00000, 0xA0000, MACHINE_ROM_ISNOTROM, NULL }, 95 | #ifndef USE_DISK_HLE 96 | { MACHINE_MEM_ROM, 0xD0000, 0x02000, MACHINE_ROM_REQUIRED, "roms/disk/ide_xt.bin" }, 97 | #endif 98 | { MACHINE_MEM_ROM, 0xFE000, 0x02000, MACHINE_ROM_REQUIRED, "roms/machine/ami_xt/ami_8088_bios_31jan89.bin" }, 99 | { MACHINE_MEM_ENDLIST, 0, 0, 0, NULL } 100 | }, 101 | 102 | //Phoenix XT clone 103 | { 104 | { MACHINE_MEM_RAM, 0x00000, 0xA0000, MACHINE_ROM_ISNOTROM, NULL }, 105 | #ifndef USE_DISK_HLE 106 | { MACHINE_MEM_ROM, 0xD0000, 0x02000, MACHINE_ROM_REQUIRED, "roms/disk/ide_xt.bin" }, 107 | #endif 108 | { MACHINE_MEM_ROM, 0xFE000, 0x02000, MACHINE_ROM_REQUIRED, "roms/machine/phoenix_xt/000p001.bin" }, 109 | { MACHINE_MEM_ENDLIST, 0, 0, 0, NULL } 110 | }, 111 | 112 | //Xi 8088 113 | { 114 | { MACHINE_MEM_RAM, 0x00000, 0xA0000, MACHINE_ROM_ISNOTROM, NULL }, 115 | #ifndef USE_DISK_HLE 116 | { MACHINE_MEM_ROM, 0xD0000, 0x02000, MACHINE_ROM_REQUIRED, "roms/disk/ide_xt.bin" }, 117 | #endif 118 | { MACHINE_MEM_ROM, 0xF0000, 0x10000, MACHINE_ROM_REQUIRED, "roms/machine/xi8088/bios128k-2.0.bin" }, //last half of this ROM is just filler for the 128k chip... 119 | { MACHINE_MEM_ENDLIST, 0, 0, 0, NULL } 120 | }, 121 | 122 | //Zenith SuperSport 8088 123 | { 124 | { MACHINE_MEM_RAM, 0x00000, 0xA0000, MACHINE_ROM_ISNOTROM, NULL }, 125 | #ifndef USE_DISK_HLE 126 | { MACHINE_MEM_ROM, 0xD0000, 0x02000, MACHINE_ROM_REQUIRED, "roms/disk/ide_xt.bin" }, 127 | #endif 128 | { MACHINE_MEM_RAM, 0xF0000, 0x04000, MACHINE_ROM_ISNOTROM, NULL }, //scratchpad RAM 129 | { MACHINE_MEM_ROM, 0xF8000, 0x08000, MACHINE_ROM_REQUIRED, "roms/machine/zenithss/z184m v3.1d.10d" }, 130 | { MACHINE_MEM_ENDLIST, 0, 0, 0, NULL } 131 | }, 132 | 133 | //Supersoft/Landmark diagnostic 134 | { 135 | { MACHINE_MEM_RAM, 0x00000, 0xA0000, MACHINE_ROM_ISNOTROM, NULL }, 136 | { MACHINE_MEM_ROM, 0xF8000, 0x08000, MACHINE_ROM_REQUIRED, "roms/machine/landmark/landmark.bin" }, 137 | { MACHINE_MEM_ENDLIST, 0, 0, 0, NULL } 138 | }, 139 | }; 140 | 141 | uint8_t mac[6] = { 0xac, 0xde, 0x48, 0x88, 0xbb, 0xab }; 142 | 143 | int machine_init_generic_xt(MACHINE_t* machine) { 144 | if (machine == NULL) return -1; 145 | 146 | i8259_init(&machine->i8259); 147 | i8253_init(&machine->i8253, &machine->i8259, &machine->pcspeaker); 148 | i8237_init(&machine->i8237, &machine->CPU); 149 | i8255_init(&machine->i8255, &machine->KeyState, &machine->pcspeaker); 150 | pcspeaker_init(&machine->pcspeaker); 151 | 152 | //check machine HW flags and init devices accordingly 153 | if ((machine->hwflags & MACHINE_HW_BLASTER) && !(machine->hwflags & MACHINE_HW_SKIP_BLASTER)) { 154 | blaster_init(&machine->blaster, &machine->i8237, &machine->i8259, 0x220, 1, 5); 155 | OPL3_init(&machine->OPL3); 156 | machine->mixBlaster = 1; 157 | machine->mixOPL = 1; 158 | } 159 | else if ((machine->hwflags & MACHINE_HW_OPL) && !(machine->hwflags & MACHINE_HW_SKIP_OPL)) { //else if because some games won't detect an SB without seeing the OPL, so if SB enabled then OPL already is 160 | //opl2_init(&machine->OPL2); 161 | OPL3_init(&machine->OPL3); 162 | machine->mixOPL = 1; 163 | } 164 | if ((machine->hwflags & MACHINE_HW_RTC) && !(machine->hwflags & MACHINE_HW_SKIP_RTC)) { 165 | rtc_init(&machine->CPU); 166 | } 167 | 168 | if ((machine->hwflags & MACHINE_HW_UART0_NONE) && !(machine->hwflags & MACHINE_HW_SKIP_UART0)) { 169 | uart_init(&machine->UART[0], &machine->i8259, 0x3F8, 4, NULL, NULL, NULL, NULL); 170 | } 171 | else if ((machine->hwflags & MACHINE_HW_UART0_MOUSE) && !(machine->hwflags & MACHINE_HW_SKIP_UART0)) { 172 | uart_init(&machine->UART[0], &machine->i8259, 0x3F8, 4, NULL, NULL, (void*)mouse_togglereset, NULL); 173 | mouse_init(&machine->UART[0]); 174 | timing_addTimer(mouse_rxpoll, NULL, baudrate / 9, TIMING_ENABLED); 175 | } 176 | #ifdef ENABLE_TCP_MODEM 177 | else if ((machine->hwflags & MACHINE_HW_UART0_TCPMODEM) && !(machine->hwflags & MACHINE_HW_SKIP_UART0)) { 178 | uart_init(&machine->UART[0], &machine->i8259, 0x3F8, 4, (void*)tcpmodem_tx, &machine->tcpmodem[0], NULL, NULL); 179 | tcpmodem_init(&machine->tcpmodem[0], &machine->UART[0], 23); 180 | timing_addTimer(tcpmodem_rxpoll, &machine->tcpmodem[0], baudrate / 9, TIMING_ENABLED); 181 | } 182 | #endif 183 | 184 | if ((machine->hwflags & MACHINE_HW_UART1_NONE) && !(machine->hwflags & MACHINE_HW_SKIP_UART1)) { 185 | uart_init(&machine->UART[1], &machine->i8259, 0x2F8, 3, NULL, NULL, NULL, NULL); 186 | } 187 | else if ((machine->hwflags & MACHINE_HW_UART1_MOUSE) && !(machine->hwflags & MACHINE_HW_SKIP_UART1)) { 188 | uart_init(&machine->UART[1], &machine->i8259, 0x2F8, 3, NULL, NULL, (void*)mouse_togglereset, NULL); 189 | mouse_init(&machine->UART[1]); 190 | timing_addTimer(mouse_rxpoll, NULL, baudrate / 9, TIMING_ENABLED); 191 | } 192 | #ifdef ENABLE_TCP_MODEM 193 | else if ((machine->hwflags & MACHINE_HW_UART1_TCPMODEM) && !(machine->hwflags & MACHINE_HW_SKIP_UART1)) { 194 | uart_init(&machine->UART[1], &machine->i8259, 0x2F8, 3, (void*)tcpmodem_tx, &machine->tcpmodem[1], NULL, NULL); 195 | tcpmodem_init(&machine->tcpmodem[1], &machine->UART[1], 23); 196 | timing_addTimer(tcpmodem_rxpoll, &machine->tcpmodem[1], baudrate / 9, TIMING_ENABLED); 197 | } 198 | #endif 199 | 200 | #ifdef USE_NE2000 201 | if (machine->hwflags & MACHINE_HW_NE2000) { 202 | ne2000_init(&machine->ne2000, &machine->i8259, 0x300, 2, (uint8_t*)&mac); 203 | if (machine->pcap_if > -1) { 204 | if (pcap_init(&machine->ne2000, machine->pcap_if)) { 205 | return -1; 206 | } 207 | } 208 | } 209 | #endif 210 | 211 | cpu_reset(&machine->CPU); 212 | #ifndef USE_DISK_HLE 213 | fdc_init(&fdc, &machine->CPU, &i8259, &i8237); 214 | fdc_insert(&fdc, 0, "dos622.img"); 215 | #else 216 | biosdisk_init(&machine->CPU); 217 | #endif 218 | 219 | switch (videocard) { 220 | case VIDEO_CARD_CGA: 221 | if (cga_init()) return -1; 222 | break; 223 | case VIDEO_CARD_VGA: 224 | if (vga_init()) return -1; 225 | break; 226 | } 227 | 228 | return 0; 229 | } 230 | 231 | int machine_init(MACHINE_t* machine, char* id) { 232 | int num = 0, match = 0, i = 0; 233 | 234 | do { 235 | if (machine_defs[num].id == NULL) { 236 | debug_log(DEBUG_ERROR, "[MACHINE] ERROR: Machine definition not found: %s\r\n", id); 237 | return -1; 238 | } 239 | 240 | if (_stricmp(id, machine_defs[num].id) == 0) { 241 | match = 1; 242 | } 243 | else { 244 | num++; 245 | } 246 | } while (!match); 247 | 248 | debug_log(DEBUG_INFO, "[MACHINE] Initializing machine: \"%s\" (%s)\r\n", machine_defs[num].description, machine_defs[num].id); 249 | 250 | //Initialize machine memory map 251 | while(1) { 252 | uint8_t* temp; 253 | if (machine_mem[num][i].memtype == MACHINE_MEM_ENDLIST) { 254 | break; 255 | } 256 | temp = (uint8_t*)malloc((size_t)machine_mem[num][i].size); 257 | if ((temp == NULL) && 258 | ((machine_mem[num][i].required == MACHINE_ROM_REQUIRED) || (machine_mem[num][i].required == MACHINE_ROM_ISNOTROM))) { 259 | debug_log(DEBUG_ERROR, "[MACHINE] ERROR: Unable to allocate %lu bytes of memory\r\n", machine_mem[num][i].size); 260 | return -1; 261 | } 262 | if (machine_mem[num][i].memtype == MACHINE_MEM_RAM) { 263 | memory_mapRegister(machine_mem[num][i].start, machine_mem[num][i].size, temp, temp); 264 | } else if (machine_mem[num][i].memtype == MACHINE_MEM_ROM) { 265 | int ret; 266 | ret = utility_loadFile(temp, machine_mem[num][i].size, machine_mem[num][i].filename); 267 | if ((machine_mem[num][i].required == MACHINE_ROM_REQUIRED) && ret) { 268 | debug_log(DEBUG_ERROR, "[MACHINE] Could not open file, or size is less than expected: %s\r\n", machine_mem[num][i].filename); 269 | return -1; 270 | } 271 | memory_mapRegister(machine_mem[num][i].start, machine_mem[num][i].size, temp, NULL); 272 | } 273 | i++; 274 | } 275 | 276 | machine->hwflags |= machine_defs[num].hwflags; 277 | 278 | if (videocard == 0xFF) { 279 | videocard = machine_defs[num].video; 280 | } 281 | 282 | if (speedarg > 0) { 283 | speed = speedarg; 284 | } else if (speedarg < 0) { 285 | speed = -1; 286 | } else { 287 | speed = machine_defs[num].speed; 288 | } 289 | 290 | if ((*machine_defs[num].init)(machine)) { //call machine-specific init routine 291 | return -1; 292 | } 293 | 294 | return num; 295 | } 296 | 297 | void machine_list() { 298 | int machine = 0; 299 | 300 | printf("Valid " STR_TITLE " machines:\r\n"); 301 | 302 | while(machine_defs[machine].id != NULL) { 303 | printf("%s: \"%s\"\r\n", machine_defs[machine].id, machine_defs[machine].description); 304 | machine++; 305 | } 306 | } 307 | --------------------------------------------------------------------------------