├── mushashi ├── example │ ├── program.bin │ ├── osd.h │ ├── osd_dos.c │ ├── sim.h │ ├── makefile │ ├── m68kconf.h │ └── sim.c ├── .gitignore ├── CMakeLists.txt ├── history.txt ├── m68kconf.h ├── readme.txt └── m68k.h ├── gemdos ├── cookiejar.h ├── net.c ├── CMakeLists.txt ├── loader.h ├── path.h ├── cookiejar.c ├── path.c ├── loader.c ├── mem.c ├── date.c ├── pexec.c ├── cdev.c ├── sys.c ├── gemdos.h ├── signals.c ├── gemdos_dispatch.c └── dir.c ├── .gitignore ├── CMakeLists.txt ├── byteorder.h ├── README.md ├── memory.i ├── memory.c ├── sysvars.h ├── tos_errors.h ├── common.h ├── tos_errors.c └── paratos.c /mushashi/example/program.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SapionAps/ParaTos/HEAD/mushashi/example/program.bin -------------------------------------------------------------------------------- /mushashi/example/osd.h: -------------------------------------------------------------------------------- 1 | #ifndef HEADER__OSD 2 | #define HEADER__OSD 3 | 4 | int osd_get_char(void); 5 | 6 | #endif /* HEADER__OSD */ 7 | -------------------------------------------------------------------------------- /gemdos/cookiejar.h: -------------------------------------------------------------------------------- 1 | 2 | void InitCookieJar(); 3 | void WriteCookie(uint32_t key, uint32_t value); 4 | int ReadCookie(uint32_t key, uint32_t* value); 5 | -------------------------------------------------------------------------------- /gemdos/net.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "tos_errors.h" 3 | 4 | /** 5 | * Nversion - 96 6 | * 7 | * Returns the identifier of the connected network 8 | */ 9 | int32_t Nversion( void ) 10 | { 11 | NOT_IMPLEMENTED(GEMDOS, Nversion, 96); 12 | return TOS_ENOSYS; 13 | } 14 | -------------------------------------------------------------------------------- /gemdos/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project (gemdos) 2 | 3 | include_directories( .. ../mushashi ) 4 | 5 | 6 | set (SOURCE_FILES cdev.c date.c dir.c file.c mem.c net.c path.c pexec.c proc.c signals.c sys.c loader.c gemdos_dispatch.c cookiejar.c) 7 | add_library (gemdos STATIC ${SOURCE_FILES}) 8 | add_definitions( -D_GNU_SOURCE ) 9 | -------------------------------------------------------------------------------- /mushashi/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | *.pbxuser 3 | !default.pbxuser 4 | *.mode1v3 5 | !default.mode1v3 6 | *.mode2v3 7 | !default.mode2v3 8 | *.perspectivev3 9 | !default.perspectivev3 10 | xcuserdata 11 | profile 12 | *.moved-aside 13 | DerivedData 14 | .DS_Store 15 | Thumbs.db 16 | .svn 17 | .BridgeSort 18 | *.bak 19 | .~* 20 | *.orig 21 | -------------------------------------------------------------------------------- /gemdos/loader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | typedef struct { 4 | uint16_t PRG_magic; 5 | int32_t PRG_tsize; 6 | int32_t PRG_dsize; 7 | int32_t PRG_bsize; 8 | int32_t PRG_ssize; 9 | int32_t PRG_res1; 10 | uint32_t PRGFLAGS; 11 | uint16_t ABSFLAG; 12 | } __attribute__((packed)) tos_header_t; 13 | 14 | emuptr32_t LoadExe(const char* filename, char* argv[], int argc); 15 | -------------------------------------------------------------------------------- /mushashi/example/osd_dos.c: -------------------------------------------------------------------------------- 1 | #include "osd.h" 2 | 3 | /* OS-dependant code to get a character from the user. 4 | * This function must not block, and must either return an ASCII code or -1. 5 | */ 6 | #include 7 | int osd_get_char(void) 8 | { 9 | int ch = -1; 10 | if(kbhit()) 11 | { 12 | while(kbhit()) 13 | ch = getch(); 14 | } 15 | return ch; 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # Debug files 32 | *.dSYM/ 33 | 34 | # Build dir 35 | build/ 36 | 37 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2.2) 2 | project(ParaTos) 3 | 4 | add_subdirectory (mushashi) 5 | add_subdirectory (gemdos) 6 | 7 | set (SOURCE_FILES paratos.c memory.c tos_errors.c) 8 | 9 | include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/mushashi ${CMAKE_CURRENT_SOURCE_DIR}/gemdos) 10 | 11 | set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11") # enable C11 standard 12 | 13 | add_executable (paratos ${SOURCE_FILES}) 14 | target_link_libraries (paratos mushashi gemdos) 15 | add_definitions( -D_GNU_SOURCE ) 16 | -------------------------------------------------------------------------------- /mushashi/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project (mushashi) 2 | 3 | set (M68KMAKE_SOURCES m68kmake.c) 4 | set (M68KMAKE_INPUT m68k_in.c) 5 | set (SOURCE_FILES m68kcpu.c m68kdasm.c) 6 | set (GEN_SOURCE_FILES m68kops.c m68kopnz.c m68kopdm.c m68kopac.c) 7 | include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ) 8 | 9 | add_executable(m68kmake ${M68KMAKE_SOURCES}) 10 | add_custom_command ( 11 | OUTPUT ${GEN_SOURCE_FILES} 12 | COMMAND m68kmake . ${CMAKE_CURRENT_SOURCE_DIR}/${M68KMAKE_INPUT} 13 | DEPENDS m68kmake 14 | ) 15 | 16 | add_library (mushashi STATIC ${SOURCE_FILES} ${GEN_SOURCE_FILES}) 17 | -------------------------------------------------------------------------------- /mushashi/example/sim.h: -------------------------------------------------------------------------------- 1 | #ifndef SIM__HEADER 2 | #define SIM__HEADER 3 | 4 | unsigned int cpu_read_byte(unsigned int address); 5 | unsigned int cpu_read_word(unsigned int address); 6 | unsigned int cpu_read_long(unsigned int address); 7 | void cpu_write_byte(unsigned int address, unsigned int value); 8 | void cpu_write_word(unsigned int address, unsigned int value); 9 | void cpu_write_long(unsigned int address, unsigned int value); 10 | void cpu_pulse_reset(void); 11 | void cpu_set_fc(unsigned int fc); 12 | int cpu_irq_ack(int level); 13 | void cpu_instr_callback(); 14 | 15 | #endif /* SIM__HEADER */ 16 | -------------------------------------------------------------------------------- /byteorder.h: -------------------------------------------------------------------------------- 1 | #ifdef __APPLE__ 2 | #include 3 | #include 4 | 5 | #define htobe16(x) OSSwapHostToBigInt16(x) 6 | #define htole16(x) OSSwapHostToLittleInt16(x) 7 | #define be16toh(x) OSSwapBigToHostInt16(x) 8 | #define le16toh(x) OSSwapLittleToHostInt16(x) 9 | 10 | #define htobe32(x) OSSwapHostToBigInt32(x) 11 | #define htole32(x) OSSwapHostToLittleInt32(x) 12 | #define be32toh(x) OSSwapBigToHostInt32(x) 13 | #define le32toh(x) OSSwapLittleToHostInt32(x) 14 | 15 | #define htobe64(x) OSSwapHostToBigInt64(x) 16 | #define htole64(x) OSSwapHostToLittleInt64(x) 17 | #define be64toh(x) OSSwapBigToHostInt64(x) 18 | #define le64toh(x) OSSwapLittleToHostInt64(x) 19 | 20 | #define __BIG_ENDIAN BIG_ENDIAN 21 | #define __LITTLE_ENDIAN LITTLE_ENDIAN 22 | #define __BYTE_ORDER BYTE_ORDER 23 | #else 24 | #include 25 | #endif 26 | 27 | -------------------------------------------------------------------------------- /gemdos/path.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define PATH_SEP(c) ((c)=='/' || (c)=='\\') 3 | #define END_OF_NAME(c) ((c)==0 || PATH_SEP(c)) 4 | 5 | // Utility function that takes a source file name and converts it to a 8.3 TOS file name. 6 | // Tos domain processes will also get the name converted to uppercase. 7 | // The destination buffer has to be 13 bytes large. 8 | const char* filename8_3(char* dest, const char* source); 9 | 10 | // Allocates a path name pointing to a valid Unix file name, mapping the source path, expanding any hashed 8.3 11 | // path components on the way and/or expanding drive names. Returns NULL if the path name could not be expanded. 12 | // The caller must deallocate the returned path using free(). 13 | char* tos_path_to_unix(char* tos_path); 14 | 15 | // Wrapper around tos_path_to_unix that takes an emulated memory location instead of a char* 16 | // As with tos_path_to_unix, the caller must deallocate the returned path using free(). 17 | char* read_path(emuptr32_t tos_path); 18 | -------------------------------------------------------------------------------- /mushashi/example/makefile: -------------------------------------------------------------------------------- 1 | EXENAME = sim 2 | 3 | OSD_DOS = osd_dos.c 4 | 5 | OSDFILES = $(OSD_DOS) 6 | MAINFILES = sim.c 7 | MUSASHIFILES = m68kcpu.c m68kdasm.c 8 | MUSASHIGENCFILES = m68kops.c m68kopac.c m68kopdm.c m68kopnz.c 9 | MUSASHIGENHFILES = m68kops.h 10 | MUSASHIGENERATOR = m68kmake 11 | 12 | EXE = .exe 13 | EXEPATH = .\\ 14 | # EXE = 15 | # EXEPATH = ./ 16 | 17 | .CFILES = $(MAINFILES) $(OSDFILES) $(MUSASHIFILES) $(MUSASHIGENCFILES) 18 | .OFILES = $(.CFILES:%.c=%.o) 19 | 20 | CC = gcc 21 | WARNINGS = -Wall -pedantic 22 | CFLAGS = $(WARNINGS) 23 | LFLAGS = $(WARNINGS) 24 | 25 | TARGET = $(EXENAME)$(EXE) 26 | 27 | DELETEFILES = $(MUSASHIGENCFILES) $(MUSASHIGENHFILES) $(.OFILES) $(TARGET) $(MUSASHIGENERATOR)$(EXE) 28 | 29 | 30 | all: $(TARGET) 31 | 32 | clean: 33 | rm -f $(DELETEFILES) 34 | 35 | $(TARGET): $(MUSASHIGENHFILES) $(.OFILES) makefile 36 | $(CC) -o $@ $(.OFILES) $(LFLAGS) 37 | 38 | $(MUSASHIGENCFILES) $(MUSASHIGENHFILES): $(MUSASHIGENERATOR)$(EXE) 39 | $(EXEPATH)$(MUSASHIGENERATOR)$(EXE) 40 | 41 | $(MUSASHIGENERATOR)$(EXE): $(MUSASHIGENERATOR).c 42 | $(CC) -o $(MUSASHIGENERATOR)$(EXE) $(MUSASHIGENERATOR).c 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ParaTos 2 | 3 | Userspace Tos (and MiNT) emulator for Linux. 4 | 5 | This is an emulator that instead of emulating ST hardware, it translates system calls into native OS calls. 6 | 7 | This should allow running clean MiNT and TOS command line tools that don't do 8 | direct HW access or attempt to patch the OS trap vectors. 9 | 10 | ParaTos uses the Mushashi m68000 emulator in order to emulate the CPU and is 11 | originally inspired by Vincent Riviere's 68kemu, which allows running applications compiled for m68020 CPU on TOS machines featuring a different physical CPU. 12 | 13 | # Building and running 14 | 15 | ParaTos uses cmake to build the project. The following should work: 16 | 17 | mkdir build 18 | cd build 19 | cmake .. 20 | make 21 | 22 | You shoyld now be able run the generated paratos executable on simple Tos executables: 23 | 24 | ./paratos hello.tos 25 | 26 | Currently, only a handful of trap #1 functions have been implemented, so you should get a lot of unimplemented function call errors. 27 | 28 | # License 29 | 30 | ParaTos is distributed under the GNU General Public License, except for Mushashi, which is licensed under 31 | a MIT-like license. 32 | 33 | The function definitions for the emulated system calls contain documentation comments extracted from 34 | http://toshyp.atari.org/en/index.html, which is also licensed under the GNU GPL. 35 | -------------------------------------------------------------------------------- /memory.i: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "byteorder.h" 9 | 10 | extern uint8_t* memory; 11 | #define INLINE __attribute__((always_inline)) static inline 12 | 13 | // NOTE we currently assume the platform allows unaligned reads and writes. 14 | #define MEM8(address) (memory[(address)]) 15 | #define MEM16(address) (*((uint16_t*)(memory+(address)))) 16 | #define MEM32(address) (*((uint32_t*)(memory+(address)))) 17 | #define MEM64(address) (*((uint64_t*)(memory+(address)))) 18 | 19 | INLINE unsigned int m68k_read_memory_8(unsigned int address) 20 | { 21 | return MEM8(address); 22 | } 23 | 24 | INLINE unsigned int m68k_read_memory_16(unsigned int address) 25 | { 26 | return be16toh(MEM16(address)); 27 | } 28 | 29 | INLINE unsigned int m68k_read_memory_32(unsigned int address) 30 | { 31 | return be32toh(MEM32(address)); 32 | } 33 | 34 | INLINE uint64_t m68k_read_memory_64(unsigned int address) 35 | { 36 | return be64toh(MEM64(address)); 37 | } 38 | 39 | INLINE void m68k_write_memory_8(unsigned int address, unsigned int value) 40 | { 41 | MEM8(address) = (uint8_t)value; 42 | } 43 | 44 | INLINE void m68k_write_memory_16(unsigned int address, unsigned int value) 45 | { 46 | MEM16(address) = htobe16((uint16_t)value); 47 | } 48 | 49 | INLINE void m68k_write_memory_32(unsigned int address, unsigned int value) 50 | { 51 | MEM32(address) = htobe32((uint32_t)value); 52 | } 53 | 54 | INLINE void m68k_write_memory_64(unsigned int address, uint64_t value) 55 | { 56 | MEM64(address) = htobe64((uint32_t)value); 57 | } 58 | -------------------------------------------------------------------------------- /memory.c: -------------------------------------------------------------------------------- 1 | #include "memory.i" 2 | 3 | uint32_t memory_sz; 4 | uint8_t* memory; 5 | int page_size; 6 | 7 | #if __x86_64__ 8 | // Allocate the entire address space addressable by a 68020 processor on 64 bit systems 9 | #define MAX_MEM 0x100000000LL 10 | #else 11 | // ... less on 32 bit (512Mb) 12 | #define MAX_MEM 0x20000000 13 | #endif 14 | 15 | void InitM68KMemory() 16 | { 17 | page_size = getpagesize(); 18 | 19 | // Reserve entire address space upfront. That way we can grow allocated memory without having to reallocate it 20 | memory = mmap(NULL, MAX_MEM, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); 21 | if (memory == MAP_FAILED) 22 | { 23 | perror("Mapping 68k memory failed: "); 24 | exit(1); 25 | } 26 | 27 | // Actually allocate first 14 Mb of that address space 28 | memory_sz = 16 * 1024 * 1024; 29 | mprotect(memory, memory_sz, PROT_READ|PROT_WRITE); 30 | 31 | // Some programs seem to want to play with some high addresses 32 | mprotect(memory + 0xff000000, 0x01000000, PROT_READ|PROT_WRITE); 33 | } 34 | 35 | unsigned int m68k_read_disassembler_8(unsigned int address) 36 | { 37 | return MEM8(address); 38 | } 39 | 40 | unsigned int m68k_read_disassembler_16 (unsigned int address) 41 | { 42 | return be16toh(MEM16(address)); 43 | } 44 | 45 | unsigned int m68k_read_disassembler_32 (unsigned int address) 46 | { 47 | return be64toh(MEM64(address)); 48 | } 49 | 50 | uint32_t m68k_write_string(uint32_t address, const char* value, uint32_t maxLen) 51 | { 52 | uint32_t i = 0; 53 | char *buffer = (char*)&memory[address]; 54 | for(i=0; i 2 | #include "common.h" 3 | #include "gemdos.h" 4 | #include "tos_errors.h" 5 | #include "sysvars.h" 6 | 7 | #include "m68k.h" 8 | #include "m68kcpu.h" 9 | 10 | uint32_t ResizeCookieJar(int32_t currentSize, int32_t newSize) 11 | { 12 | uint32_t oldJar = m68k_read_memory_32(_p_cookies); 13 | uint32_t newJar = Malloc(newSize * 8); 14 | for(uint32_t slot = 0; slot < currentSize; slot++) 15 | { 16 | uint32_t key = m68k_read_memory_32(oldJar+slot*8); 17 | m68k_write_memory_32(newJar+slot*8, key); 18 | if (key) 19 | { 20 | m68k_write_memory_32(newJar+slot*8+4, m68k_read_memory_32(oldJar+slot*8+4)); 21 | } 22 | else 23 | { 24 | m68k_write_memory_32(newJar+slot*8+4, newSize); 25 | break; 26 | } 27 | } 28 | m68k_write_memory_32(_p_cookies, newJar); 29 | Mfree(oldJar); 30 | // TRACEF("RESIZE %d@%x->%d@%x\n", currentSize, oldJar, newSize, newJar); 31 | return newJar; 32 | } 33 | 34 | void WriteCookie(uint32_t key, uint32_t value) 35 | { 36 | uint32_t base = m68k_read_memory_32(_p_cookies); 37 | uint32_t address = base; 38 | int found = 0; 39 | int currentCount = 0; 40 | int maxCount = 0; 41 | uint32_t current=0; 42 | for(;;address += 8) 43 | { 44 | current = m68k_read_memory_32(address); 45 | currentCount++; 46 | if (current == 0) 47 | { 48 | maxCount = m68k_read_memory_32(address+4); 49 | break; 50 | } 51 | 52 | if (current == key) 53 | { 54 | found = 1; 55 | break; 56 | } 57 | } 58 | if(!found && currentCount == maxCount) 59 | { 60 | maxCount *= 2; 61 | uint32_t newBase = ResizeCookieJar(currentCount, maxCount); 62 | address += newBase - base; 63 | base = newBase; 64 | } 65 | // TRACEF("Write %0x @ %0x (%0x)\n",key, address, m68k_read_memory_32(address) ); 66 | m68k_write_memory_32(address, key); 67 | address += 4; 68 | // TRACEF("Write %0x @ %0x\n",value, address); 69 | m68k_write_memory_32(address, value); 70 | address += 4; 71 | if(!found) 72 | { 73 | m68k_write_memory_32(address, 0); 74 | address += 4; 75 | m68k_write_memory_32(address, maxCount); 76 | return; 77 | } 78 | } 79 | 80 | int ReadCookie(uint32_t key, uint32_t* value) 81 | { 82 | uint32_t address = m68k_read_memory_32(_p_cookies); 83 | int found = 0; 84 | if (key > 1 && key <= 0xffff) 85 | { 86 | for(int32_t i=1; i <= key; address += 8, i++) 87 | { 88 | if (m68k_read_memory_32(address) == 0) 89 | { 90 | return 0; 91 | } 92 | } 93 | *value=m68k_read_memory_32(address); 94 | return 1; 95 | } 96 | else 97 | { 98 | uint32_t current=0; 99 | for(;;address += 8) 100 | { 101 | current = m68k_read_memory_32(address); 102 | if (current == 0) 103 | { 104 | break; 105 | } 106 | 107 | if (current == key) 108 | { 109 | found = 1; 110 | break; 111 | } 112 | } 113 | } 114 | 115 | if (found) 116 | *value=m68k_read_memory_32(address+4); 117 | return found-1; 118 | } 119 | 120 | 121 | void InitCookieJar() 122 | { 123 | int numCookies = 0x100; 124 | uint32_t base = Malloc(numCookies * 8); 125 | TRACEF("InitCookieJar: %08x\n", base); 126 | m68k_write_memory_32(_p_cookies, base); 127 | m68k_write_memory_32(base, 0); 128 | m68k_write_memory_32(base+4, numCookies); 129 | WriteCookie(0x4d694e54, 0x200); // MiNT 130 | WriteCookie(0x5f435055, 20); // _CPU => 20 131 | WriteCookie(0x5f465055, 0); // _FPU => none 132 | WriteCookie(0x5f4d4348, 0x60000); // _MCH => 6/0 (paraTos specific) 133 | } 134 | -------------------------------------------------------------------------------- /sysvars.h: -------------------------------------------------------------------------------- 1 | /* definitions for various GEMDOS system variables in low memory */ 2 | /* WARNING: this file is not compatible with the old one */ 3 | 4 | /* 5 | * Convention: 6 | * the address for each variable is defined. 7 | */ 8 | #pragma once 9 | 10 | /* timer, crit error and process termination handoff vectors */ 11 | #define etv_timer ((void (**)()) 0x400L) 12 | #define etv_critic ((void (**)()) 0x404L) 13 | #define etv_term ((void (**)()) 0x408L) 14 | 15 | /* memory controller */ 16 | #define MEMVALID_MAGIC 0x752019F3L /* once memory is sized */ 17 | #define memvalid (/* (unsigned long *) */ 0x420L) 18 | #define mencntlr (/* (unsigned char *) */ 0x424L) 19 | /* 0 = 128K, 4 = 512K 0 = 256k(2banks) 5 = 1M */ 20 | 21 | /* reset vector, jump through resvector if resvalid on reset */ 22 | #define RESVALID_MAGIC 0x31415926L 23 | #define resvalid (/* (unsigned long *) */ 0x426L) 24 | #define resvector ((void (**)()) 0x42aL) 25 | /* do a jmp 0x24L(a6) at end to go to system reset */ 26 | 27 | /* mem */ 28 | #define phystop (/* (unsigned long *) */ 0x42eL) /* physical top of st ram */ 29 | #define _membot (/* (unsigned long *) */ 0x432L) /* bottom of avail */ 30 | #define _memtop (/* (unsigned long *) */ 0x436L) /* top of avail */ 31 | #define MEMVAL2_MAGIC 0x237698AAL /* after suc. coldstart && memvalid */ 32 | #define memval2 (/* (unsigned long *) */ 0x43aL) 33 | #define ramtop (/* (unsigned long *) */ 0x5a4L) /* physical top of tt ram */ 34 | 35 | /* floppy */ 36 | #define flock (/* (short *) */ 0x43eL) /* lock usage of DMA chip */ 37 | #define seekrate (/* (short *) */ 0x440L) /* 0=6ms 1=12ms 2=2ms 3=3ms */ 38 | #define _timr_ms (/* (short *) */ 0x442L) /* timer calib == 20ms */ 39 | #define _fverify (/* (short *) */ 0x444L) /* write verify flag */ 40 | #define _bootdev (/* (short *) */ 0x446L) 41 | 42 | /* video */ 43 | #define palmode (/* (short *) */ 0x448L) /* PAL video mode flag */ 44 | #define defshiftmd (/* (unsigned char *) */ 0x44aL) /* default video rez */ 45 | #define sshiftmd (/* (short *) */ 0x44cL) /* shadow of hdwr. shiftmd reg */ 46 | /* 0=Lo 1=med 2=Hi rez */ 47 | #define _v_bas_ad (/* (void *) */ 0x44eL) /* screen mem base */ 48 | #define vblsem (/* (short *) */ 0x452L) /* vbl semaphore */ 49 | #define nvbls (/* (short *) */ 0x454L) /* # of vbl entries def. == 8 */ 50 | #define _vblqueue ((void (***)()) 0x456L) /* vbl queue pointer */ 51 | #define colorptr ((short **) 0x45aL) /* pal. on next vblank if!NULL */ 52 | #define _vbclock (/* (unsigned long *) */ 0x462L) /* vbi counter */ 53 | #define _frclock (/* (unsigned long *) */ 0x466L) /* #vbi not vblsem'ed */ 54 | 55 | #define _hz_200 (/* (unsigned long *) */ 0x4baL) 56 | 57 | #define conterm (*/* (char *) */ 0x484L) 58 | #define savptr (/* (long *) */ 0x4A2L) 59 | #define _nflops (/* (short *) */ 0x4A6L) 60 | #define _sysbase (/* (long *) */ 0x4F2L) 61 | #define _shell_p (/* (long *) */ 0x4F6L) 62 | 63 | typedef struct { 64 | short puns; 65 | char v_p_un[16]; 66 | long pstart[16]; 67 | short bpbs[1]; /* really 16 BPB's (bios parameter block) */ 68 | } HDINFO; 69 | 70 | #define pun_ptr (/* (HDINFO *) */ 0x516L) 71 | #define _p_cookies (/* (long **) */ 0x5A0L) 72 | 73 | /* os header */ 74 | typedef struct _osheader 75 | { 76 | unsigned short os_entry; /* 0x00 BRA to reset handler */ 77 | unsigned short os_version; /* 0x02 TOS version */ 78 | void (*reseth) (void); /* 0x04 -> reset handler */ 79 | struct _osheader *os_beg; /* 0x08 -> base of OS */ 80 | void *os_end; /* 0x0c -> end of OS ram usage */ 81 | char *os_rsv1; /* 0x10 reserved */ 82 | char *os_magic; /* 0x14 GEM memory usage param */ 83 | long os_date; /* 0x18 Build date 0xMMDDYYYY */ 84 | unsigned short os_conf; /* 0x1c OS conf bits */ 85 | unsigned short os_dosdate; /* 0x1e DOS format build date */ 86 | /* the following available on TOS version >= 1.2 */ 87 | char **p_root; /* 0x20 -> base of OS pool */ 88 | char **pkbshift; /* 0x24 -> kbd shift state var */ 89 | char **p_run; /* 0x28 -> PID of current proc */ 90 | char *p_rsv2; /* 0x2c reserved */ 91 | } OSHEADER; 92 | -------------------------------------------------------------------------------- /tos_errors.h: -------------------------------------------------------------------------------- 1 | #define TOS_E_OK 0 2 | #define TOS_ERROR -1 3 | #define TOS_EDRVNR -2 4 | #define TOS_EUNCMD -3 5 | #define TOS_E_CRC -4 6 | #define TOS_EBADRQ -5 7 | #define TOS_E_SEEK -6 8 | #define TOS_EMEDIA -7 9 | #define TOS_ESECNF -8 10 | #define TOS_EPAPER -9 11 | #define TOS_EWRITF -10 12 | #define TOS_EREADF -11 13 | #define TOS_EGENRL -12 14 | #define TOS_EWRPRO -13 15 | #define TOS_E_CHNG -14 16 | #define TOS_EUNDEV -15 17 | #define TOS_EBADSF -16 18 | #define TOS_EOTHER -17 19 | #define TOS_EINSERT -18 20 | #define TOS_EDVNRSP -19 21 | #define TOS_ESRCH -20 22 | #define TOS_ECHILD -21 23 | #define TOS_EDEADLK -22 24 | #define TOS_ENOTBLK -23 25 | #define TOS_EISDIR -24 26 | #define TOS_EINVAL -25 27 | #define TOS_EFTYPE -26 28 | #define TOS_EILSEQ -27 29 | #define TOS_ENOSYS -32 30 | #define TOS_EINVFN -32 31 | #define TOS_EFILNF -33 32 | #define TOS_EPTHNF -34 33 | #define TOS_ENHNDL -35 34 | #define TOS_EACCDN -36 35 | #define TOS_EBADF -37 36 | #define TOS_EIHNDL TOS_EBADF 37 | #define TOS_EPERM -38 38 | #define TOS_ENSMEM -39 39 | #define TOS_EIMBA -40 40 | #define TOS_EDRIVE -46 41 | #define TOS_ECWD -47 42 | #define TOS_ENSAME -48 43 | #define TOS_ENMFIL -49 44 | #define TOS_ENFILE -50 45 | #define TOS_ELOCKED -58 46 | #define TOS_ENSLOCK -59 47 | #define TOS_EBADARG -64 48 | #define TOS_EINTRN -65 49 | #define TOS_ENOEXEC -66 50 | #define TOS_EPLFMT TOS_ENOEXEC 51 | #define TOS_EGSBF -67 52 | #define TOS_EBREAK -68 53 | #define TOS_EXCPT -69 54 | #define TOS_EPTHOV -70 55 | #define TOS_EFBIG -71 56 | #define TOS_ELOOP -80 57 | #define TOS_EPIPE -81 58 | #define TOS_EMLINK -82 59 | #define TOS_ENOTEMPTY -83 60 | #define TOS_EEXIST -85 61 | #define TOS_ENAMETOOLONG -86 62 | #define TOS_ENOTTY -87 63 | #define TOS_ERANGE -88 64 | #define TOS_EDOM -89 65 | #define TOS_EIO -90 66 | #define TOS_ENOSPC -91 67 | #define TOS_EPROCLIM -100 68 | #define TOS_EUSERS -101 69 | #define TOS_EDQUOT -102 70 | #define TOS_ESTALE -103 71 | #define TOS_EREMOTE -104 72 | #define TOS_EBADRPC -105 73 | #define TOS_ERPCMISMATCH -106 74 | #define TOS_EPROGUNAVAIL -107 75 | #define TOS_EPROGMISMATCH -108 76 | #define TOS_EPROCUNAVAIL -109 77 | #define TOS_ENOLCK -110 78 | #define TOS_EAUTH -111 79 | #define TOS_ENEEDAUTH -112 80 | #define TOS_EBACKGROUND -113 81 | #define TOS_EBADMSG -114 82 | #define TOS_EIDRM -115 83 | #define TOS_EMULTIHOP -116 84 | #define TOS_ENODATA -117 85 | #define TOS_ENOLINK -118 86 | #define TOS_ENOMSG -119 87 | #define TOS_ENOSR -120 88 | #define TOS_ENOSTR -121 89 | #define TOS_EOVERFLOW -122 90 | #define TOS_EPROTO -123 91 | #define TOS_ETIME -124 92 | #define TOS_E2BIG -125 93 | #define TOS_ERESTART -126 94 | #define TOS_ECHRNG -127 95 | #define TOS_SNDNOTLOCK -128 96 | #define TOS_SNDLOCKED -129 97 | #define TOS_ESNDNOTLOCK -130 98 | #define TOS_EL2NSYNC -131 99 | #define TOS_EL3HLT -132 100 | #define TOS_EL3RST -133 101 | #define TOS_ELNRNG -134 102 | #define TOS_EUNATCH -135 103 | #define TOS_ENOCSI -136 104 | #define TOS_EL2HLT -137 105 | #define TOS_EBADE -138 106 | #define TOS_EXFULL -139 107 | #define TOS_ENOANO -140 108 | #define TOS_EBADRQC -141 109 | #define TOS_EBADSLT -142 110 | #define TOS_EBFONT -143 111 | #define TOS_ENONET -144 112 | #define TOS_ENOPKG -145 113 | #define TOS_EADV -146 114 | #define TOS_ESRMNT -147 115 | #define TOS_ECOMM -148 116 | #define TOS_EDOTDOT -149 117 | #define TOS_ELIBACC -150 118 | #define TOS_ELIBBAD -151 119 | #define TOS_ELIBSCN -152 120 | #define TOS_ELIBMAX -153 121 | #define TOS_ELIBEXEC -154 122 | #define TOS_ESTRPIPE -155 123 | #define TOS_EUCLEAN -156 124 | #define TOS_ENOTNAM -157 125 | #define TOS_ENAVAIL -158 126 | #define TOS_EREMOTEIO -159 127 | #define TOS_EMOUNT -200 128 | #define TOS_ENOTSOCK -300 129 | #define TOS_EDESTADDRREQ -301 130 | #define TOS_EMSGSIZE -302 131 | #define TOS_EPROTOTYPE -303 132 | #define TOS_ENOPROTOOPT -304 133 | #define TOS_EPROTONOSUPPORT -305 134 | #define TOS_ESOCKTNOSUPPORT -306 135 | #define TOS_EOPNOTSUPP -307 136 | #define TOS_EPFNOSUPPORT -308 137 | #define TOS_EAFNOSUPPORT -309 138 | #define TOS_EADDRINUSE -310 139 | #define TOS_EADDRNOTAVAIL -311 140 | #define TOS_ENETDOWN -312 141 | #define TOS_ENETUNREACH -313 142 | #define TOS_ENETRESET -314 143 | #define TOS_ECONNABORTED -315 144 | #define TOS_ECONNRESET -316 145 | #define TOS_EISCONN -317 146 | #define TOS_ENOTCONN -318 147 | #define TOS_ESHUTDOWN -319 148 | #define TOS_ETIMEDOUT -320 149 | #define TOS_ECONNREFUSED -321 150 | #define TOS_EHOSTDOWN -322 151 | #define TOS_EHOSTUNREACH -323 152 | #define TOS_EALREADY -324 153 | #define TOS_EINPROGRESS -325 154 | #define TOS_EAGAIN -326 155 | #define TOS_ENOBUFS -327 156 | #define TOS_ETOOMANYREFS -328 157 | 158 | int32_t MapErrno(); 159 | -------------------------------------------------------------------------------- /common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifdef __APPLE__ 3 | #include 4 | #include 5 | 6 | #define htobe16(x) OSSwapHostToBigInt16(x) 7 | #define htole16(x) OSSwapHostToLittleInt16(x) 8 | #define be16toh(x) OSSwapBigToHostInt16(x) 9 | #define le16toh(x) OSSwapLittleToHostInt16(x) 10 | 11 | #define htobe32(x) OSSwapHostToBigInt32(x) 12 | #define htole32(x) OSSwapHostToLittleInt32(x) 13 | #define be32toh(x) OSSwapBigToHostInt32(x) 14 | #define le32toh(x) OSSwapLittleToHostInt32(x) 15 | 16 | #define htobe64(x) OSSwapHostToBigInt64(x) 17 | #define htole64(x) OSSwapHostToLittleInt64(x) 18 | #define be64toh(x) OSSwapBigToHostInt64(x) 19 | #define le64toh(x) OSSwapLittleToHostInt64(x) 20 | 21 | #define __BIG_ENDIAN BIG_ENDIAN 22 | #define __LITTLE_ENDIAN LITTLE_ENDIAN 23 | #define __BYTE_ORDER BYTE_ORDER 24 | #endif // __APPLE__ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include "m68k.h" 31 | #include "m68kcpu.h" 32 | #include "tos_errors.h" 33 | 34 | extern size_t memory_sz; 35 | extern uint8_t* memory; 36 | 37 | typedef uint32_t emuptr32_t; 38 | typedef uint32_t emureg_t; 39 | 40 | void InitM68KMemory(); 41 | 42 | #ifdef DEBUG 43 | #define TRACEF(...) fprintf(stderr, __VA_ARGS__); 44 | #else 45 | #define TRACEF(...) 46 | #endif 47 | 48 | #define NOT_IMPLEMENTED(Subsystem, Name, Op) fprintf(stderr, "Unimplemented " #Subsystem " call " #Name "(%d/%x)\n", Op, Op ); 49 | uint64_t m68k_read_memory_64(unsigned int address); 50 | void m68k_write_memory_64(unsigned int address, uint64_t value); 51 | uint32_t m68k_write_string(uint32_t address, const char* value, uint32_t maxLen); 52 | void m68k_read_array8(uint32_t address, char* dest, uint32_t count); 53 | void m68k_write_array8(uint32_t address, const char* value, uint32_t count); 54 | 55 | #define m68k_write(address, value) \ 56 | _Generic((value), \ 57 | uint64_t: m68k_write_memory_64((address),(value)), \ 58 | int64_t: m68k_write_memory_64((address),(value)), \ 59 | uint32_t: m68k_write_memory_32((address),(value)), \ 60 | int32_t: m68k_write_memory_32((address),(value)), \ 61 | uint16_t: m68k_write_memory_16((address),(value)), \ 62 | int16_t: m68k_write_memory_16((address),(value)), \ 63 | uint8_t: m68k_write_memory_8((address),(value)), \ 64 | int8_t: m68k_write_memory_8((address),(value)), \ 65 | int8_t[sizeof(value)] : m68k_write_array8((address),(char*)(intptr_t)(value),sizeof(value)), \ 66 | int8_t* : m68k_write_string((address),(char*)(intptr_t)(value),1024) \ 67 | ) 68 | 69 | #define m68k_read(address, typed_dummy_value) \ 70 | _Generic((typed_dummy_value), \ 71 | uint64_t: m68k_read_memory_64((address)), \ 72 | int64_t: m68k_read_memory_64((address)), \ 73 | uint32_t: m68k_read_memory_32((address)), \ 74 | int32_t: m68k_read_memory_32((address)), \ 75 | uint16_t: m68k_read_memory_16((address)), \ 76 | int16_t: m68k_read_memory_16((address)), \ 77 | uint8_t: m68k_read_memory_8((address)), \ 78 | int8_t: m68k_read_memory_8((address)) \ 79 | ) 80 | 81 | void m68k_read_to_64(uint32_t address, uint64_t* dest); 82 | void m68k_read_to_32(uint32_t address, uint32_t* dest); 83 | void m68k_read_to_16(uint32_t address, uint16_t* dest); 84 | void m68k_read_to_8(uint32_t address, uint8_t* dest); 85 | 86 | #define m68k_read_to(address, lval) ({\ 87 | _Generic((lval), \ 88 | uint64_t: m68k_read_to_64((address),(uint64_t*)&(lval)), \ 89 | int64_t: m68k_read_to_64((address),(uint64_t*)&(lval)), \ 90 | uint32_t: m68k_read_to_32((address),(uint32_t*)&(lval)), \ 91 | int32_t: m68k_read_to_32((address),(uint32_t*)&(lval)), \ 92 | uint16_t: m68k_read_to_16((address),(uint16_t*)&(lval)), \ 93 | int16_t: m68k_read_to_16((address),(uint16_t*)&(lval)), \ 94 | uint8_t: m68k_read_to_8((address),(uint8_t*)&(lval)), \ 95 | int8_t: m68k_read_to_8((address),(uint8_t*)&(lval)), \ 96 | int8_t[sizeof(lval)] : m68k_read_array8((address),(char*)(intptr_t)(lval),sizeof(lval)), \ 97 | );}) 98 | 99 | #define m68k_write_field(address, type, field, value) ({ \ 100 | m68k_write((address) + offsetof(type, field), (typeof(((type *)0)->field))(value)); \ 101 | }) 102 | 103 | #define m68k_write_struct_member(address, value, field) \ 104 | m68k_write((address) + offsetof(typeof(value), field), (value).field) 105 | 106 | #define m68k_read_field(address, type, field) \ 107 | m68k_read((address) + offsetof(type, field), (((type *)0)->field)) 108 | 109 | #define m68k_read_field_to(address, type, field, dest) \ 110 | m68k_read_to((address) + offsetof(type, field), dest ) 111 | 112 | #define m68k_read_struct_member(address, value, field) \ 113 | m68k_read_field_to((address), typeof(value), field, (value).field) 114 | -------------------------------------------------------------------------------- /tos_errors.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "tos_errors.h" 8 | #include 9 | 10 | int32_t MapErrno() 11 | { 12 | switch (errno) 13 | { 14 | case 0: 15 | return TOS_E_OK; 16 | case ENOENT: 17 | return TOS_EFILNF; 18 | case ESRCH: 19 | return TOS_ESRCH; 20 | case EBADF: 21 | return TOS_EBADF; 22 | case ECHILD: 23 | return TOS_ECHILD; 24 | case EDEADLK: 25 | return TOS_EDEADLK; 26 | case ENOTBLK: 27 | return TOS_ENOTBLK; 28 | case EISDIR: 29 | return TOS_EISDIR; 30 | case EINVAL: 31 | return TOS_EINVAL; 32 | case ENOEXEC: 33 | return TOS_ENOEXEC; 34 | case EILSEQ: 35 | return TOS_EILSEQ; 36 | case ENOSYS: 37 | return TOS_ENOSYS; 38 | case EACCES: 39 | return TOS_EACCDN; 40 | case EPERM: 41 | return TOS_EPERM; 42 | case ENFILE: 43 | return TOS_ENFILE; 44 | case EFBIG: 45 | return TOS_EFBIG; 46 | case ELOOP: 47 | return TOS_ELOOP; 48 | case EPIPE: 49 | return TOS_EPIPE; 50 | case EMLINK: 51 | return TOS_EMLINK; 52 | case ENOTEMPTY: 53 | return TOS_ENOTEMPTY; 54 | case EEXIST: 55 | return TOS_EEXIST; 56 | case ENAMETOOLONG: 57 | return TOS_ENAMETOOLONG; 58 | case ENOTTY: 59 | return TOS_ENOTTY; 60 | case ERANGE: 61 | return TOS_ERANGE; 62 | case EDOM: 63 | return TOS_EDOM; 64 | case EIO: 65 | return TOS_EIO; 66 | case ENOSPC: 67 | return TOS_ENOSPC; 68 | case EUSERS: 69 | return TOS_EUSERS; 70 | case EDQUOT: 71 | return TOS_EDQUOT; 72 | case ESTALE: 73 | return TOS_ESTALE; 74 | case EREMOTE: 75 | return TOS_EREMOTE; 76 | case ENOLCK: 77 | return TOS_ENOLCK; 78 | case EBADMSG: 79 | return TOS_EBADMSG; 80 | case EIDRM: 81 | return TOS_EIDRM; 82 | case EMULTIHOP: 83 | return TOS_EMULTIHOP; 84 | case ENODATA: 85 | return TOS_ENODATA; 86 | case ENOLINK: 87 | return TOS_ENOLINK; 88 | case ENOMSG: 89 | return TOS_ENOMSG; 90 | case ENOSR: 91 | return TOS_ENOSR; 92 | case ENOSTR: 93 | return TOS_ENOSTR; 94 | case EOVERFLOW: 95 | return TOS_EOVERFLOW; 96 | case EPROTO: 97 | return TOS_EPROTO; 98 | case ETIME: 99 | return TOS_ETIME; 100 | case E2BIG: 101 | return TOS_E2BIG; 102 | #ifdef LINUX 103 | case ERESTART: 104 | return TOS_ERESTART; 105 | case ECHRNG: 106 | return TOS_ECHRNG; 107 | case EL2NSYNC: 108 | return TOS_EL2NSYNC; 109 | case EL3HLT: 110 | return TOS_EL3HLT; 111 | case EL3RST: 112 | return TOS_EL3RST; 113 | case ELNRNG: 114 | return TOS_ELNRNG; 115 | case EUNATCH: 116 | return TOS_EUNATCH; 117 | case ENOCSI: 118 | return TOS_ENOCSI; 119 | case EL2HLT: 120 | return TOS_EL2HLT; 121 | case EBADE: 122 | return TOS_EBADE; 123 | case EXFULL: 124 | return TOS_EXFULL; 125 | case ENOANO: 126 | return TOS_ENOANO; 127 | case EBADSLT: 128 | return TOS_EBADSLT; 129 | case EBFONT: 130 | return TOS_EBFONT; 131 | case ENONET: 132 | return TOS_ENONET; 133 | case ENOPKG: 134 | return TOS_ENOPKG; 135 | case EADV: 136 | return TOS_EADV; 137 | case ESRMNT: 138 | return TOS_ESRMNT; 139 | case ECOMM: 140 | return TOS_ECOMM; 141 | case EDOTDOT: 142 | return TOS_EDOTDOT; 143 | case ELIBACC: 144 | return TOS_ELIBACC; 145 | case ELIBBAD: 146 | return TOS_ELIBBAD; 147 | case ELIBSCN: 148 | return TOS_ELIBSCN; 149 | case ELIBMAX: 150 | return TOS_ELIBMAX; 151 | case ELIBEXEC: 152 | return TOS_ELIBEXEC; 153 | case ESTRPIPE: 154 | return TOS_ESTRPIPE; 155 | case EUCLEAN: 156 | return TOS_EUCLEAN; 157 | case ENOTNAM: 158 | return TOS_ENOTNAM; 159 | case ENAVAIL: 160 | return TOS_ENAVAIL; 161 | case EREMOTEIO: 162 | return TOS_EREMOTEIO; 163 | #endif 164 | case ENOTSOCK: 165 | return TOS_ENOTSOCK; 166 | case EDESTADDRREQ: 167 | return TOS_EDESTADDRREQ; 168 | case EMSGSIZE: 169 | return TOS_EMSGSIZE; 170 | case EPROTOTYPE: 171 | return TOS_EPROTOTYPE; 172 | case ENOPROTOOPT: 173 | return TOS_ENOPROTOOPT; 174 | case EPROTONOSUPPORT: 175 | return TOS_EPROTONOSUPPORT; 176 | case ESOCKTNOSUPPORT: 177 | return TOS_ESOCKTNOSUPPORT; 178 | case EOPNOTSUPP: 179 | return TOS_EOPNOTSUPP; 180 | case EPFNOSUPPORT: 181 | return TOS_EPFNOSUPPORT; 182 | case EAFNOSUPPORT: 183 | return TOS_EAFNOSUPPORT; 184 | case EADDRINUSE: 185 | return TOS_EADDRINUSE; 186 | case EADDRNOTAVAIL: 187 | return TOS_EADDRNOTAVAIL; 188 | case ENETDOWN: 189 | return TOS_ENETDOWN; 190 | case ENETUNREACH: 191 | return TOS_ENETUNREACH; 192 | case ENETRESET: 193 | return TOS_ENETRESET; 194 | case ECONNABORTED: 195 | return TOS_ECONNABORTED; 196 | case ECONNRESET: 197 | return TOS_ECONNRESET; 198 | case EISCONN: 199 | return TOS_EISCONN; 200 | case ENOTCONN: 201 | return TOS_ENOTCONN; 202 | case ESHUTDOWN: 203 | return TOS_ESHUTDOWN; 204 | case ETIMEDOUT: 205 | return TOS_ETIMEDOUT; 206 | case ECONNREFUSED: 207 | return TOS_ECONNREFUSED; 208 | case EHOSTDOWN: 209 | return TOS_EHOSTDOWN; 210 | case EHOSTUNREACH: 211 | return TOS_EHOSTUNREACH; 212 | case EALREADY: 213 | return TOS_EALREADY; 214 | case EINPROGRESS: 215 | return TOS_EINPROGRESS; 216 | case EAGAIN: 217 | return TOS_EAGAIN; 218 | case ENOBUFS: 219 | return TOS_ENOBUFS; 220 | case ETOOMANYREFS: 221 | return TOS_ETOOMANYREFS; 222 | default: 223 | fprintf(stderr, "Unmapped errno %d ", errno); 224 | perror(NULL); 225 | return TOS_ERROR; 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /gemdos/path.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "common.h" 11 | #include "gemdos.h" 12 | #include "path.h" 13 | 14 | const char* filename8_3(char* dest, const char* source) 15 | { 16 | bool needsHash = false; 17 | bool uppercase = !m68k_read_field(current_process, basepage_t, mint_domain); 18 | int slen = 0; const char* retval; 19 | for(slen=0;!END_OF_NAME(source[slen]);slen++); 20 | retval = source + slen; 21 | 22 | char* d=dest; 23 | if (source[0] == '.' && ( slen == 1 || slen == 2 && source[1] == '.')) 24 | { 25 | // special case for . and .. 26 | for(int i = 0; i=source; c--) 37 | { 38 | if (*c == '.') 39 | { 40 | dot=c; 41 | break; 42 | } 43 | } 44 | 45 | // If last dot is at the front of the filename, treat as no extension 46 | const char* s=source; 47 | if (dot == source) 48 | { 49 | dot=0; 50 | } 51 | 52 | // If any parts of the file name don't fit withing the 8.3 limits, we'll need to mangle the filename 53 | if(slen > 12 || dot && ((dot-source) > 8 || (slen - (dot-source)) > 4) ||!dot && slen > 8) 54 | { 55 | needsHash=true; 56 | } 57 | 58 | for(int i=0; i<8; i++) 59 | { 60 | if(END_OF_NAME(*s) || (dot && s==dot)) 61 | { 62 | // make room for the hash 63 | if(needsHash) 64 | { 65 | for(int j=i; j<8; j++) 66 | *d++='~'; 67 | } 68 | break; 69 | } 70 | if( *s == '.') 71 | { 72 | *d++='_'; s++; 73 | needsHash=true; 74 | } 75 | else 76 | { 77 | *d++=uppercase?toupper(*s++):*s++; 78 | } 79 | } 80 | 81 | if(dot) 82 | { 83 | *d++='.'; 84 | s=dot+1; 85 | for(int i=0; i<3; i++) 86 | { 87 | if(END_OF_NAME(*s)) 88 | break; 89 | *d++=uppercase?toupper(*s++):*s++; 90 | } 91 | } 92 | *d=0; 93 | 94 | if(needsHash) 95 | { 96 | static const char hash_alphabet[32] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345"; 97 | uint16_t hash=0xf00f; 98 | for (s=source; !END_OF_NAME(s[1]); s++) 99 | hash = (hash << 3) ^ (hash >> 5) ^ *s ^ (s[1] << 8); 100 | hash = (hash << 3) ^ (hash >> 5) ^ *s; 101 | dest[4]='~'; 102 | dest[5]=hash_alphabet[(hash>>10)&0x1f]; 103 | dest[6]=hash_alphabet[(hash>>5)&0x1f]; 104 | dest[7]=hash_alphabet[hash&0x1f]; 105 | } 106 | return retval; 107 | } 108 | 109 | char* read_path(emuptr32_t tos_path) 110 | { 111 | return tos_path_to_unix(&memory[tos_path]); 112 | } 113 | 114 | static int next_path_sep(char* path, int start) 115 | { 116 | while(!END_OF_NAME(path[start])) 117 | { 118 | start++; 119 | } 120 | return start; 121 | } 122 | 123 | // Helper to append to the result string and resizing the buffer when required 124 | #define append(src, offset, len) ({ \ 125 | const char* s = (src); \ 126 | int o = (offset); \ 127 | int l = (len); \ 128 | bool reallocate = false; \ 129 | while (result_sz < o + l + 1) \ 130 | {\ 131 | result_sz *= 2; \ 132 | reallocate = true; \ 133 | }\ 134 | if (reallocate) \ 135 | { \ 136 | result = realloc(result, result_sz); \ 137 | if (!result) \ 138 | return NULL; /* out of memory */ \ 139 | } \ 140 | memcpy(&result[o], s, l); \ 141 | result[o+l] = '\0'; \ 142 | result_len = o+l; \ 143 | }) 144 | 145 | char* tos_path_to_unix(char* tos_path) 146 | { 147 | size_t tos_len = strlen(tos_path); 148 | if(tos_len >= 2 && tos_path[1] == ':') 149 | { 150 | // Todo: support multiple drive mappings 151 | if (tos_path[0] == 'u' || tos_path[0] == 'U') 152 | { 153 | tos_path+=2; 154 | tos_len-=2; 155 | } 156 | else 157 | { 158 | return NULL; 159 | } 160 | } 161 | size_t result_sz = tos_len * 2; 162 | size_t result_len = 0; 163 | char* result = malloc(result_sz); 164 | if (PATH_SEP(tos_path[0])) 165 | { 166 | append("/", 0, 1); 167 | } 168 | 169 | struct stat st; 170 | char short_name[13]; 171 | for(int i=0, j=next_path_sep(tos_path, 0) ; tos_path[i]; i=j+1, j=next_path_sep(tos_path, j+1)) 172 | { 173 | size_t current = result_len; 174 | 175 | if(i==j) 176 | continue; 177 | append(&tos_path[i], current, j-i); 178 | if (lstat(result, &st) == -1) 179 | { 180 | // Attempt scanning the parent directory for the curent component 181 | char* curname = &result[current]; 182 | const char* curdir = "."; 183 | if (current) 184 | { 185 | result[current-1]=0; 186 | curdir = current==1?"/":result; 187 | } 188 | DIR* dir = opendir(curdir); 189 | if (!dir) 190 | { 191 | free(result); 192 | return NULL; 193 | } 194 | 195 | bool found = false; 196 | struct dirent* entry; 197 | while(entry = readdir(dir)) 198 | { 199 | if(strcasecmp(curname, entry->d_name) == 0) 200 | { 201 | goto match; 202 | } 203 | if(j-i <= 12) 204 | { 205 | filename8_3(short_name, entry->d_name); 206 | if(strcasecmp(curname, short_name) == 0) 207 | { 208 | goto match; 209 | } 210 | } 211 | continue; 212 | match: 213 | found = true; 214 | if(current) 215 | { 216 | result[current-1]='/'; 217 | } 218 | append(entry->d_name, current, strlen(entry->d_name)); 219 | break; 220 | } 221 | closedir(dir); 222 | if (!found) 223 | { 224 | if(!tos_path[j]) // if it's the last path component simply return the unmodified path name 225 | { 226 | if(current) 227 | { 228 | result[current-1]='/'; 229 | } 230 | } 231 | else 232 | { 233 | free(result); 234 | return NULL; 235 | } 236 | } 237 | } 238 | if(!tos_path[j]) 239 | { 240 | break; 241 | } 242 | append("/", result_len, 1); 243 | } 244 | 245 | return result; 246 | } 247 | 248 | #undef append 249 | -------------------------------------------------------------------------------- /mushashi/history.txt: -------------------------------------------------------------------------------- 1 | The history of Musashi for anyone who might be interested: 2 | --------------------------------------------------------- 3 | 4 | Musashi was born out of sheer boredom. 5 | I needed something to code, and so having had fun with a few of the emulators 6 | around, I decided to try my hand at CPU emulation. 7 | I had owned an Amiga for many years and had done some assembly coding on it so 8 | I figured it would be the ideal chip to cut my teeth on. 9 | Had I known then how much work was involved in emulating a chip like this, I 10 | may not have even started ;-) 11 | 12 | 13 | 15-Jul-2013: Musashi license changed to MIT. 14 | 15 | 10-Jun-2002: Musashi 3.4 released 16 | - Added various undocumented m68k features thanks to Bart 17 | Trzynadlowski's experiments. 18 | See http://dynarec.com/~bart/files/68knotes.txt for details. 19 | - Fixed a bug that caused privilege violation and illegal 20 | instruction exceptions to stack the wrong PC value. 21 | - Added emulation of address errors (Note: this only works 22 | in 68000 mode. All other CPUs require a LOT of overhead 23 | to emulate this. I'm not sure if I'll implement them or not. 24 | 25 | 27-Jan-2001: Musashi 3.3 released 26 | - Fixed problem when displaying negative numbers in disassembler 27 | - Fixed cpu type selector - was allowing 020 instructions to be 28 | disassembled when in 000 mode. 29 | - Fixed opcode jumptable generator (ambiguous operators in the 30 | test for f-line ops) 31 | - Fixed signed/unsigned problem in divl and mull opcodes (not 32 | sure if this was causing an error but best to be sure) 33 | - Cleaned up the naming scheme for the opcode handlers 34 | 35 | 14-Aug-2000: Musashi 3.2 released 36 | - Fixed RTE bug that killed the program counter when in m68020 37 | mode. 38 | - Minor fixes in negx and nbcd. 39 | - renamed d68k.c to m68kdasm.c and merged d68k.h into m68k.h. 40 | d68k_read_xxx() instructions have been renamed to 41 | m68k_read_xxx_disassembler(). 42 | - Rewrote exception processing and fixed 68020 stack frame 43 | problems. 44 | - FINALLY fixed the mull and divl instructions. 45 | - Added 64-bit safe code fixes. 46 | - Added 64-bit optimizations (these will only be ANSI compliant 47 | under c9x, and so to use them you must turn on M68K_USE_64_BIT 48 | in m68kconf.h). 49 | 50 | 28-May-2000: Musashi 3.1 released 51 | - Fixed bug in m68k_get_reg() that retrieved the wrong value for 52 | the status register. 53 | - Fixed register bug in movec. 54 | - Fixed cpu type comparison problem that caused indexed 55 | addressing modes to be incorrectly interpreted when in m68ec020 56 | mode. 57 | - Added code to speed up busy waiting on some branch instructions. 58 | - Fixed some bfxxx opcode bugs. 59 | 60 | 05-Apr-2000: Musashi 3.0 released 61 | - Major code overhaul. 62 | - Rewrote code generator program and changed the format of 63 | m68k_in.c. 64 | - Added support for m68ec020. 65 | - Removed timing from the opcode handlers. 66 | - Added correct timing for m68000, m68010, and m68020. 67 | Note: 68020 timing is the cache timing from the manual. 68 | - Removed the m68k_peek_xxx() and m68k_poke_xxx() instructions and 69 | replaced them with m68k_get_reg() and m68k_set_reg(). 70 | - Added support for function codes. 71 | - Revamped m68kconf.h to be easier to configure and more powerful. 72 | - Added option to separate immediate and normal reads. 73 | - Added support for (undocumented) m68000 instruction prefetch. 74 | - Rewrote indexed addressing mode handling. 75 | - Rewrote interrupt handling. 76 | - Fixed a masking bug for m68k_get_reg() when requesting the PC. 77 | - Moved the instruction table sorting routine to m68kmake.c so 78 | that it is invoked at compile time rather than at runtime. 79 | - Rewrote the exception handling routines to support different 80 | stack frames (needed for m68020 emulation). 81 | - Rewrote faster status register and condition code flag handling 82 | functions / macros. 83 | - Fixed function code handling to fetch from program space when 84 | using pc-relative addressing. 85 | - Fixed initial program counter and stack pointer fetching on 86 | reset (loads from program space now). 87 | - A lot of code cleanup. 88 | - LOTS of bugfixes (especially in the m68020 code). 89 | 90 | 13-May-1999: Musashi 2.2 released 91 | - Added support for m68020. 92 | - Lots of bugfixes. 93 | 94 | 25-Mar-1999: Musashi 2.1 released 95 | - Added support for m68010. 96 | - Many bugfixes. 97 | 98 | 17-Mar-1999: Musashi 2.0 released 99 | - Major code overhaul. 100 | - Replaced monolithic codebase with a code generator program. 101 | - Added correct m68000 timing. 102 | - Moved timing into the opcode handlers. 103 | 104 | 06-Jan-1999: Musashi 1.0 released 105 | 106 | 20-Dec-1998: Beta release of Musashi v0.5 that could run Rastan Saga under MAME 107 | (barely). 108 | 109 | 04-Dec-1998: Final prototype v0.4 110 | 111 | 20-Nov-1998: First prototype v0.1 112 | 113 | 11-Jun-1998: Early disassembler 114 | 115 | 12-May-1998: First outline 116 | -------------------------------------------------------------------------------- /gemdos/loader.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "common.h" 7 | #include "byteorder.h" 8 | #include "m68k.h" 9 | #include "m68kcpu.h" 10 | #include "gemdos.h" 11 | #include "loader.h" 12 | 13 | extern char **environ; 14 | 15 | #define MAX_TOS_ARG 126 16 | 17 | uint32_t BuildTosArglist(uint32_t args, char* argv[], int argc, uint32_t env) 18 | { 19 | int n = 1; 20 | char* arg_buffer = &memory[args]; 21 | for (int i = 1; i < argc; ++i) 22 | { 23 | if (i > 0) 24 | { 25 | arg_buffer[n++] = ' '; 26 | if (n>MAX_TOS_ARG) 27 | goto trunc; 28 | } 29 | if(argv[i][0]==0) 30 | { 31 | arg_buffer[n++] = '\''; 32 | if (n>MAX_TOS_ARG) 33 | { 34 | n--; 35 | goto trunc; 36 | } 37 | arg_buffer[n++] = '\''; 38 | if (n>MAX_TOS_ARG) 39 | goto trunc; 40 | 41 | } 42 | else 43 | { 44 | for(char* c = argv[i]; *c; c++) 45 | { 46 | arg_buffer[n++] = *c; 47 | if (n>MAX_TOS_ARG) 48 | goto trunc; 49 | } 50 | } 51 | } 52 | arg_buffer[0] = n-1; 53 | goto notrunc; 54 | trunc: 55 | arg_buffer[n] = '\0'; 56 | arg_buffer[0] = 127; 57 | notrunc: 58 | 59 | // ARGV procedure - pass complete command line at the end of the env string 60 | env+=m68k_write_string(env, "ARGV=", 5); 61 | // Check for empty parameters and enable extended ARGV procedure in that case 62 | bool nullAdded = false; 63 | char buffer[21]; // buffer for sprintf 64 | for (int i = 0; i < argc; ++i) 65 | { 66 | if(argv[i][0]==0) 67 | { 68 | snprintf(buffer, 21, nullAdded?",%d":"NULL:%d", i); 69 | nullAdded = true; 70 | env+=m68k_write_string(env, buffer, 20); 71 | } 72 | } 73 | m68k_write_memory_8(env++, '\0'); 74 | for (int i = 0; i < argc; ++i) 75 | { 76 | if(argv[i][0]==0) 77 | { 78 | m68k_write_memory_8(env++, ' '); 79 | } 80 | else 81 | { 82 | env+=m68k_write_string(env, argv[i], 0x7fffffff); 83 | } 84 | m68k_write_memory_8(env++, '\0'); 85 | } 86 | 87 | 88 | return env; 89 | } 90 | 91 | 92 | #define WRITE_BASEPAGE(field,value) m68k_write_field(base_tpa, basepage_t, field, value) 93 | #define READ_BASEPAGE(field) m68k_read_field(base_tpa, basepage_t, field) 94 | 95 | emuptr32_t LoadExe(const char* filename, char* argv[], int argc) 96 | { 97 | tos_header_t header; 98 | FILE* file = fopen(filename,"r"); 99 | if (!file) 100 | { 101 | perror(filename); 102 | return 0; 103 | } 104 | fread(&header, sizeof(tos_header_t), 1, file); 105 | header.PRG_magic = be16toh(header.PRG_magic); 106 | header.PRG_tsize = be32toh(header.PRG_tsize); 107 | header.PRG_dsize = be32toh(header.PRG_dsize); 108 | header.PRG_bsize = be32toh(header.PRG_bsize); 109 | header.PRG_ssize = be32toh(header.PRG_ssize); 110 | header.PRG_res1 = be32toh(header.PRG_res1); 111 | header.PRGFLAGS = be32toh(header.PRGFLAGS); 112 | header.ABSFLAG = be16toh(header.ABSFLAG); 113 | TRACEF("Reading in text size: %d, data: %d, magic: %04x\n", header.PRG_tsize, header.PRG_dsize, header.PRG_magic); 114 | 115 | uint32_t base_tpa = Malloc(Malloc(-1)); 116 | 117 | WRITE_BASEPAGE(p_lowtpa, base_tpa); 118 | WRITE_BASEPAGE(p_hitpa, memory_sz); 119 | 120 | uint32_t current = base_tpa + sizeof(basepage_t); 121 | WRITE_BASEPAGE(p_env, current); /* Address of the environment string */ 122 | 123 | // copy environment 124 | for(char ** ep=environ; *ep; ep++) 125 | { 126 | for(char *vp=*ep; *vp;vp++) 127 | { 128 | m68k_write_memory_8(current++, *vp); 129 | } 130 | m68k_write_memory_8(current++, 0); 131 | 132 | } 133 | // Copy command line 134 | uint32_t command_line = base_tpa+offsetof(basepage_t, p_cmdlin); 135 | current = BuildTosArglist(command_line, argv, argc, current); 136 | 137 | m68k_write_memory_8(current++, 0); 138 | while((current & 3) !=0) 139 | current++; 140 | 141 | // Set up rest of base page (offset is 32 bit aligned, so we can access the 32 bit values directly) 142 | WRITE_BASEPAGE(p_tbase, current); /* Start address of the program code */ 143 | WRITE_BASEPAGE(p_tlen, header.PRG_tsize); /* Length of the program code */ 144 | current += header.PRG_tsize; 145 | WRITE_BASEPAGE(p_dbase, current); /* Start address of the DATA segment */ 146 | WRITE_BASEPAGE(p_dlen, header.PRG_dsize); /* Length of the DATA section */ 147 | current += header.PRG_dsize; 148 | WRITE_BASEPAGE(p_bbase, current); /* Start address of the BSS segment */ 149 | WRITE_BASEPAGE(p_blen, header.PRG_bsize); /* Length of the BSS section */ 150 | current += header.PRG_bsize; 151 | WRITE_BASEPAGE(p_dta, command_line); /* Pointer to the default DTA */ 152 | /* Warning: Points first to the */ 153 | /* command line ! */ 154 | WRITE_BASEPAGE(p_parent, 0); /* Pointer to the basepage of the */ 155 | /* calling processes */ 156 | 157 | TRACEF("Reading in text and data from executable: %d+%d at address %d\n", READ_BASEPAGE(p_tlen), READ_BASEPAGE(p_dlen), READ_BASEPAGE(p_tbase)); 158 | char* dest = memory+READ_BASEPAGE(p_tbase); 159 | if(!fread(dest, sizeof(char), READ_BASEPAGE(p_tlen)+READ_BASEPAGE(p_dlen), file)) 160 | { 161 | return 0; 162 | } 163 | TRACEF("Skipping %d bytes of symbol data\n", header.PRG_ssize); 164 | fseek(file, header.PRG_ssize, SEEK_CUR); 165 | uint8_t delta=0; 166 | uint32_t fixup=0; 167 | uint32_t unfixed; 168 | uint32_t fixed; 169 | if (fread(&fixup, sizeof(uint32_t), 1, file)) 170 | { 171 | fixup = be32toh(fixup); 172 | TRACEF("Performing fixups starting at offset %08x\n", fixup); 173 | if (fixup) 174 | { 175 | fixup += READ_BASEPAGE(p_tbase); 176 | do { 177 | unfixed = m68k_read_memory_32(fixup); 178 | fixed = unfixed + READ_BASEPAGE(p_tbase); 179 | 180 | //TRACEF("Fixing up %08x->%08x at address %08x\n",unfixed, fixed, fixup); 181 | m68k_write_memory_32(fixup, fixed); 182 | delta=0; 183 | do { 184 | fread(&delta, sizeof(uint8_t), 1, file); 185 | fixup += delta == 1?254:delta; 186 | } while(delta==1); 187 | } while(delta != 0); 188 | } 189 | } 190 | 191 | return base_tpa; 192 | } 193 | -------------------------------------------------------------------------------- /gemdos/mem.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "common.h" 6 | #include "tos_errors.h" 7 | #include "sysvars.h" 8 | 9 | typedef struct memblock { 10 | struct memblock* next; 11 | emuptr32_t start; 12 | int32_t size; 13 | } memblock_t; 14 | 15 | memblock_t freeMem; 16 | memblock_t usedMem; 17 | memblock_t* rover; 18 | 19 | void InitMemory() 20 | { 21 | m68k_write_memory_32(_membot, 0x10000); 22 | m68k_write_memory_32(_memtop, memory_sz-1); 23 | m68k_write_memory_32(phystop, memory_sz); 24 | usedMem.next = NULL; 25 | usedMem.start = -1; 26 | usedMem.size = -1; 27 | memblock_t* firstBlock = malloc(sizeof(memblock_t)); 28 | firstBlock->start = 0x10000; 29 | firstBlock->size = memory_sz-firstBlock->start; 30 | firstBlock->next = NULL; 31 | freeMem.next = firstBlock; 32 | freeMem.start = -1; 33 | freeMem.size = -1; 34 | rover = &freeMem; 35 | } 36 | 37 | /** 38 | * Maddalt - 20 39 | * 40 | * The GEMDOS routine Maddalt permits the inclusion of a block of additional 41 | * alternate-RAM in the GEMDOS memory list that would not normally have been 42 | * identified by the system. The following apply: Parameter Meaning 43 | * 44 | * int32_t Maddalt ( void *start, int32_t size ) 45 | */ 46 | int32_t Maddalt ( emuptr32_t start, int32_t size ) 47 | { 48 | NOT_IMPLEMENTED(GEMDOS, Maddalt, 20); 49 | return TOS_ENOSYS; 50 | } 51 | 52 | /** 53 | * Malloc - 72 54 | * 55 | * The GEMDOS routine Malloc reserves a block of memory for an application, 56 | * or calculates the size of the largest free block of memory. The following 57 | * applies for the parameter number: Value Meaning 58 | * 59 | * void *Malloc ( int32_t number ) 60 | */ 61 | emuptr32_t Malloc ( int32_t size ) 62 | { 63 | uint32_t max = 0; 64 | if (size != -1) 65 | { 66 | size = (size + 3) & ~3; 67 | } 68 | 69 | if (freeMem.next == NULL) 70 | return 0; // Out of memory 71 | 72 | memblock_t* prev = rover; 73 | memblock_t* current = prev->next; 74 | do { 75 | if (current == NULL) // wrap around 76 | { 77 | prev = &freeMem; 78 | current = prev->next; 79 | } 80 | if (size == -1) 81 | { 82 | if (max < current->size) 83 | max = current->size; 84 | } 85 | else if(current->size >= size) 86 | { 87 | if (current->size == size) 88 | { 89 | prev->next = current->next; 90 | } 91 | else 92 | { 93 | memblock_t* new = malloc(sizeof(memblock_t)); 94 | prev->next = new; 95 | new->next = current->next; 96 | new->start = current->start + size; 97 | new->size = current->size - size; 98 | current->size = size; 99 | } 100 | rover = (prev == &freeMem?prev->next:prev); 101 | current->next = usedMem.next; 102 | usedMem.next = current; 103 | return current->start; 104 | } 105 | current = (prev=current)->next; 106 | } while(prev != rover); 107 | 108 | return max; 109 | } 110 | 111 | // Returns a free block back to the free list 112 | static void freeBlock(memblock_t* freeme) 113 | { 114 | memblock_t* prev=NULL; 115 | memblock_t* current; 116 | for(current = freeMem.next; current; current = (prev=current)->next) 117 | if(freeme->start <= current->start) 118 | break; 119 | freeme->next = current; 120 | if(prev) 121 | { 122 | prev->next=freeme; 123 | } 124 | else 125 | { 126 | freeMem.next=freeme; 127 | } 128 | 129 | if(!rover) 130 | rover = freeme; 131 | 132 | if(current && freeme->start + freeme->size == current->start) 133 | { 134 | freeme->size += current->size; 135 | freeme->next = current->next; 136 | if (rover == current) 137 | rover = freeme; 138 | free(current); 139 | } 140 | 141 | if(prev && prev->start + prev->size == freeme->start) 142 | { 143 | prev->size += freeme->size; 144 | prev->next = freeme->next; 145 | if (rover == freeme) 146 | rover = prev; 147 | free(freeme); 148 | } 149 | 150 | #if 0 151 | TRACEF("Free blocks:\n"); 152 | for(memblock_t* c=freeMem.next; c; c =c->next) 153 | { 154 | TRACEF("\t%06x - %06x (sz %x)\n", c->start, c->start+c->size-1,c->size); 155 | } 156 | #endif 157 | } 158 | 159 | /** 160 | * Mfree - 73 161 | * 162 | * The GEMDOS routine Mfree releases a block of memory previously reserved 163 | * with Malloc. 164 | * 165 | * The parameter block contains the start address of the memory block to be 166 | * released. 167 | * 168 | * Note: In almost all GEMDOS versions no check is made whether the block to 169 | * be released really belongs to the relevant process. Hence particular care 170 | * is needed, specially in multitasking systems. 171 | * 172 | * int32_t Mfree ( void *block ) 173 | */ 174 | int32_t Mfree ( emuptr32_t block ) 175 | { 176 | memblock_t* prev = &usedMem; 177 | for(memblock_t* current=usedMem.next; current; current = (prev=current)->next) 178 | { 179 | if(current->start == block) 180 | { 181 | prev->next = current->next; 182 | freeBlock(current); 183 | return TOS_E_OK; 184 | } 185 | } 186 | return TOS_EIMBA; 187 | } 188 | 189 | /** 190 | * Mshrink - 74 191 | * 192 | * The GEMDOS routine Mshrink reduces or enlarges an already reserved memory 193 | * block in the GEMDOS free list. The following apply: Parameter Meaning 194 | * 195 | * int32_t Mshrink ( void *block, int32_t newsiz ) 196 | */ 197 | int32_t Mshrink ( emuptr32_t block, int32_t newsiz ) 198 | { 199 | if(newsiz == 0) 200 | return Mfree(block); 201 | newsiz = (newsiz + 3) & ~3; 202 | for(memblock_t* current=usedMem.next; current; current = current->next) 203 | { 204 | if(current->start == block) 205 | { 206 | if(current->size < newsiz) 207 | { 208 | return TOS_EGSBF; 209 | } 210 | memblock_t* new = malloc(sizeof(memblock_t)); 211 | new->start = current->start+newsiz; 212 | new->size = current->size-newsiz; 213 | current->size=newsiz; 214 | freeBlock(new); 215 | return newsiz; 216 | } 217 | } 218 | return TOS_EIMBA; 219 | } 220 | 221 | /** 222 | * Mxalloc - 68 223 | * 224 | * The GEMDOS routine Mxalloc reserves a block of memory of the size amount. 225 | * One can use the bits of the WORD parameter mode to specify the desired 226 | * type of memory. The following apply: Bits Meaning 227 | * 228 | * void *Mxalloc ( int32_t amount, int16_t mode ) 229 | */ 230 | emuptr32_t Mxalloc ( int32_t amount, int16_t mode ) 231 | { 232 | NOT_IMPLEMENTED(GEMDOS, Mxalloc, 68); 233 | return TOS_ENOSYS; 234 | } 235 | -------------------------------------------------------------------------------- /gemdos/date.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "common.h" 4 | #include "tos_errors.h" 5 | 6 | uint32_t unixtime2dos(time_t* unixtime) 7 | { 8 | struct tm t; 9 | if(localtime_r(unixtime, &t) == NULL) 10 | { 11 | return 0; 12 | } 13 | return (t.tm_mday) | ((t.tm_mon+1)<<5) | (((t.tm_year-80)&0x7f)<<9) | 14 | ((((t.tm_sec/2)&0xF) | (t.tm_min << 5) | (t.tm_hour << 11)) << 16); 15 | } 16 | 17 | time_t dostime2unix(uint32_t dostime) 18 | { 19 | struct tm t; 20 | uint16_t timestamp = dostime >> 16; 21 | uint16_t datestamp = dostime & 0xffff; 22 | 23 | t.tm_sec = (timestamp & 0x1f) * 2; 24 | t.tm_min = (timestamp >> 5) & 0x3f; 25 | t.tm_hour = (timestamp >> 11) & 0x1f; 26 | 27 | t.tm_mday = (datestamp & 0x1f); 28 | t.tm_mon = ((datestamp >> 5) & 0xf) - 1; 29 | t.tm_year = 80 + ((datestamp >> 9) & 0x7f); 30 | 31 | return mktime(&t); 32 | } 33 | /** 34 | * Talarm - 288 35 | * 36 | * The function Talarm sends a SIGALRM signal to the calling process after 37 | * time seconds. 38 | * 39 | * If no signal-handler was installed for this signal with Psignal, then the 40 | * process will be terminated immediately (i.e. on receiving the signal). 41 | * 42 | * By setting the value of time = 0, an earlier set alarm can be cancelled. 43 | * If time is negative, then the function has no effect and only the return 44 | * value is delivered. 45 | */ 46 | int32_t Talarm ( int32_t time ) 47 | { 48 | NOT_IMPLEMENTED(GEMDOS, Talarm, 288); 49 | return TOS_ENOSYS; 50 | } 51 | 52 | /** 53 | * Tgetdate - 42 54 | * 55 | * The GEMDOS routine Tgetdate obtains the current date. 56 | */ 57 | uint32_t Tgetdate ( void ) 58 | { 59 | time_t current=time(NULL); 60 | return unixtime2dos(¤t) & 0xffff; 61 | } 62 | 63 | /** 64 | * Tgettime - 44 65 | * 66 | * The GEMDOS routine Tgettime obtains the system time. 67 | */ 68 | uint32_t Tgettime ( void ) 69 | { 70 | time_t current=time(NULL); 71 | return (unixtime2dos(¤t)>>16) & 0xffff; 72 | } 73 | 74 | /** 75 | * Tgettimeofday - 341 76 | * 77 | * The function Tgettimeofday interrogates the state of the internal, high 78 | * resolution system clock. 79 | * 80 | * The argument tv is a pointer to the timeval structure. 81 | * 82 | * The argument tzp is a pointer to timezone structure. 83 | * 84 | * You may safely pass NULL for either argument. This isn't considered an 85 | * error. 86 | * 87 | * Implementors of library bindings should be aware that the definition of 88 | * struct timezone is non-standard. The members are actually int and not long 89 | * int (this applies only to struct timezone; the members of struct timeval 90 | * are always long). 16-bit libraries will have to copy the contents of the 91 | * structure that tzp points to. 92 | * 93 | * The Ssystem() call has a command S_CLOCKMODE. This command allows to 94 | * retrieve or set the kernel clock mode, i.e. to specify whether the 95 | * hardware clock is meant to run in UTC or in local time. 96 | * It is planned to make MiNT compliant with the kernel time keeping model 97 | * described in RFC1305. This model is already successfully implemented in 98 | * operating systems such as SunOS, Ultrix, OSF/1, HP-UX and Linux. Please 99 | * expect the internal realization to change in the future. 100 | * 101 | * int32_t Tgettimeofday ( timeval *tv, timezone *tzp ) 102 | */ 103 | int32_t Tgettimeofday ( emuptr32_t tv, emuptr32_t tzp ) 104 | { 105 | struct timeval timeval; 106 | struct timezone timezone; 107 | if( 0 == gettimeofday(&timeval, &timezone)) 108 | { 109 | if( tv ) 110 | { 111 | m68k_write_memory_32(tv, timeval.tv_sec); 112 | m68k_write_memory_32(tv+4, timeval.tv_usec); 113 | } 114 | if ( tzp ) 115 | { 116 | m68k_write_memory_32(tzp, timezone.tz_minuteswest); 117 | m68k_write_memory_32(tzp+4, timezone.tz_dsttime); 118 | } 119 | } 120 | return TOS_E_OK; 121 | } 122 | 123 | /** 124 | * Tmalarm - 317 125 | * 126 | * This function reads/sets a process alarm for the current process. 127 | * 128 | * time specifies the length of time (in milliseconds) to wait before a 129 | * SIGALRM signal is delivered. If time is 0 then any previously set alarm is 130 | * cancelled. If time is negative the function does not modify any alarm 131 | * currently set. 132 | * 133 | * If no SIGALRM signal handler has been set up when the alarm is triggered, 134 | * the process will be killed. 135 | */ 136 | int32_t Tmalarm( int32_t time ) 137 | { 138 | NOT_IMPLEMENTED(GEMDOS, Tmalarm, 317); 139 | return TOS_ENOSYS; 140 | } 141 | 142 | /** 143 | * Tsetdate - 43 144 | * 145 | * The GEMDOS routine Tsetdate sets the current date. The parameter date is 146 | * coded as follows: Bits Meaning 147 | */ 148 | int16_t Tsetdate ( uint16_t date ) 149 | { 150 | NOT_IMPLEMENTED(GEMDOS, Tsetdate, 43); 151 | return TOS_ENOSYS; 152 | } 153 | 154 | /** 155 | * Tsetitimer - 329 156 | * 157 | * The GEMDOS routine Tsetitimer controls one of three possible periodic 158 | * timers. The following apply: Parameter Meaning 159 | * 160 | * int32_t Tsetitimer ( int16_t which, int32_t *interval, int32_t *value, int32_t *ointerval, int32_t *ovalue ) 161 | */ 162 | int32_t Tsetitimer ( int16_t which, emuptr32_t interval, emuptr32_t value, emuptr32_t ointerval, emuptr32_t ovalue ) 163 | { 164 | NOT_IMPLEMENTED(GEMDOS, Tsetitimer, 329); 165 | return TOS_ENOSYS; 166 | } 167 | 168 | /** 169 | * Tsettime - 45 170 | * 171 | * The GEMDOS routine Tsettime sets the time. The parameter time must be 172 | * coded as follows: Bits Meaning 173 | */ 174 | int16_t Tsettime ( uint16_t time ) 175 | { 176 | NOT_IMPLEMENTED(GEMDOS, Tsettime, 45); 177 | return TOS_ENOSYS; 178 | } 179 | 180 | /** 181 | * Tsettimeofday - 342 182 | * 183 | * The function Tsettimeofday sets the state of the internal, high resolution 184 | * system clock. 185 | * 186 | * The argument tv is a pointer to the timeval structure. 187 | * 188 | * The argument tzp is a pointer to the timezone structure. 189 | * 190 | * You may safely pass NULL for either argument. This isn't considered an 191 | * error. 192 | * 193 | * Implementors of library bindings should be aware that the definition of 194 | * struct timezone is non-standard. The members are actually int and not long 195 | * int (this applies only to struct timezone; the members of struct timeval 196 | * are always long). 16-bit libraries will have to copy the contents of the 197 | * structure that tzp points to. 198 | * 199 | * The tz_dsttime member of timezone is stored, but not evaluated within the 200 | * kernel. Beware not to misunderstand its meaning: if non-zero it simply 201 | * signifies that daylight savings time applies during some part of the year, 202 | * not necessarily now. In other words: if it is non-zero someday, it should 203 | * be non-zero during the entire year. The Ssystem call has a command 204 | * S_CLOCKMODE. This command allows retrieval or setting of the kernel clock 205 | * mode, i.e. to specify whether the hardware clock is meant to run in UTC or 206 | * in local time.(!nl) It is planned to make MiNT compliant with the kernel 207 | * timekeeping model described in RFC1305. This model is already successfully 208 | * implemented in operating systems such as SunOS, Ultrix, OSF/1, HP-UX and 209 | * Linux. Please expect the internal realization to change in the future. 210 | * 211 | * int32_t Tsettimeofday ( timeval *tv, timezone *tzp ) 212 | */ 213 | int32_t Tsettimeofday ( emuptr32_t tv, emuptr32_t tzp ) 214 | { 215 | NOT_IMPLEMENTED(GEMDOS, Tsettimeofday, 342); 216 | return TOS_ENOSYS; 217 | } 218 | -------------------------------------------------------------------------------- /gemdos/pexec.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "common.h" 12 | #include "path.h" 13 | #include "tos_errors.h" 14 | #include "gemdos.h" 15 | 16 | extern char* paratos_exe; 17 | 18 | /** 19 | * reads a list of zero terminated strings, terminated by an empty string at the end, and allocates an array of strng pointers 20 | * the result must be released using free 21 | */ 22 | static char** read_env(emuptr32_t address, size_t* out_size) 23 | { 24 | size_t array_count=0; 25 | size_t current_strsz=0; 26 | size_t src_chars = 0; 27 | 28 | char* src = (char*)memory+address; 29 | 30 | do { 31 | char c = src[src_chars++]; 32 | if (c == 0) 33 | { 34 | if(current_strsz == 0) 35 | { 36 | break; 37 | } 38 | else 39 | { 40 | array_count++; 41 | current_strsz=0; 42 | } 43 | } 44 | else 45 | { 46 | current_strsz++; 47 | } 48 | } while(true); 49 | 50 | // note we allocate one extra entry in case we need extra room for the paratos executable name 51 | size_t ptr_arraysz = (array_count+2) * sizeof(char*); 52 | char** result = malloc(ptr_arraysz); 53 | 54 | char* current = src; 55 | int j=0; 56 | for(int i=0; i 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "common.h" 11 | #include "m68k.h" 12 | #include "m68kcpu.h" 13 | #include "gemdos.h" 14 | #include "loader.h" 15 | #include "sysvars.h" 16 | #include "cookiejar.h" 17 | 18 | void dispatch_line_f() 19 | { 20 | fprintf(stderr, "FPU is not emulated\n"); 21 | } 22 | 23 | void dispatch_line_a() 24 | { 25 | uint32_t pc = m68k_get_reg(NULL, M68K_REG_PC); 26 | uint16_t ins = m68k_get_reg(NULL, M68K_REG_IR); 27 | printf("LINE-A %04x pc=0x%08x\n", ins, pc); 28 | m68k_set_reg(M68K_REG_D0, 0); 29 | m68k_set_reg(M68K_REG_A0, 0); 30 | m68k_set_reg(M68K_REG_A1, 0); 31 | m68k_set_reg(M68K_REG_A2, 0); 32 | } 33 | 34 | void dispatch_gem_trap() 35 | { 36 | uint32_t op = m68k_get_reg(NULL, M68K_REG_D0); 37 | emuptr32_t pblock = m68k_get_reg(NULL, M68K_REG_D1); 38 | 39 | emuptr32_t ctrl = m68k_read_memory_32(pblock); 40 | 41 | switch (op) { 42 | case 0x73: 43 | { 44 | uint16_t vdiop = m68k_read_memory_16(ctrl); 45 | uint16_t ptsinc = m68k_read_memory_16(ctrl+2); 46 | uint16_t intinc = m68k_read_memory_16(ctrl+6); 47 | uint16_t subop = m68k_read_memory_16(ctrl+10); 48 | uint16_t handle = m68k_read_memory_16(ctrl+12); 49 | m68k_write_memory_16(ctrl+4, 0); 50 | m68k_write_memory_16(ctrl+8, 0); 51 | m68k_write_memory_16(ctrl+12, 0); 52 | printf("VDI pblock=%08x op=%d,%d handle=%d, #pt=%d, #int=%d\n", pblock, vdiop, subop, handle, ptsinc, intinc); 53 | break; 54 | } 55 | case 0xc8: 56 | { 57 | uint16_t aesop = m68k_read_memory_16(ctrl); 58 | emuptr32_t global = m68k_read_memory_32(pblock+4); 59 | emuptr32_t intin = m68k_read_memory_32(pblock+8); 60 | emuptr32_t intout = m68k_read_memory_32(pblock+12); 61 | emuptr32_t adrin = m68k_read_memory_32(pblock+16); 62 | emuptr32_t adrout = m68k_read_memory_32(pblock+20); 63 | switch (aesop) { 64 | case 120: // shel_read 65 | m68k_write_memory_16(intout+0, 1); 66 | emuptr32_t cmd = m68k_read_memory_32(adrin+0); 67 | emuptr32_t cmdln = m68k_read_memory_32(adrin+4); 68 | printf("AES pblock=%0x, op=shel_read/%d (%x,%x)\n", pblock, aesop, cmd, cmdln); 69 | m68k_write_memory_32(cmd, m68k_read_field(current_process, basepage_t, p_env)); 70 | m68k_write_memory_32(cmdln, current_process + offsetof(basepage_t, p_cmdlin)); 71 | printf("%s %s\n", &memory[m68k_read_memory_32(cmd)], &memory[m68k_read_memory_32(cmdln)]); 72 | break; 73 | default: 74 | printf("AES pblock=%0x, op=%d\n", pblock, aesop); 75 | break; 76 | 77 | } 78 | break; 79 | } 80 | default: 81 | printf("Unknown trap#2 pblock=%0x\n", pblock); 82 | break; 83 | } 84 | } 85 | 86 | void dispatch_bios_trap() 87 | { 88 | uint32_t sp = m68k_get_reg(NULL, M68K_REG_SP); 89 | uint16_t num = m68k_read_memory_16(sp); 90 | 91 | if (num == 3) 92 | { 93 | printf("BCONOUT(%d, %c)\n", m68k_read_memory_16(sp-2), 0x7f&m68k_read_memory_16(sp-4)); 94 | } 95 | else { 96 | printf("BIOS(0x%02x)\n", num); 97 | } 98 | 99 | 100 | m68k_set_reg(M68K_REG_D0, -(int)num); 101 | } 102 | 103 | 104 | void dispatch_xbios_trap() 105 | { 106 | uint32_t sp = m68k_get_reg(NULL, M68K_REG_SP); 107 | int16_t num = m68k_read_memory_16(sp); 108 | int16_t retval = -num; 109 | switch(num) 110 | { 111 | case 4: // getrez 112 | retval = 3; // ST Mono 113 | break; 114 | case 17: // random 115 | retval = rand() & 0x00ffffff; 116 | break; 117 | case 38: // Supexec 118 | // We're just going to fake it an run it in user mode anyway 119 | { 120 | uint32_t call = m68k_read_memory_32(sp+2); 121 | uint32_t callee = m68k_get_reg(NULL, M68K_REG_PC); 122 | m68k_write_memory_32(sp-4, callee); 123 | m68k_set_reg(M68K_REG_SP, sp-4); 124 | m68k_set_reg(M68K_REG_PC, call); 125 | return; 126 | } 127 | default: 128 | printf("XBIOS(0x%02x)\n", num); 129 | } 130 | 131 | m68k_set_reg(M68K_REG_D0, retval); 132 | } 133 | 134 | emuptr32_t current_process; 135 | char* paratos_exe; 136 | 137 | void FindExecutable(void) 138 | { 139 | size_t pathSize = 1024; 140 | while(true) { 141 | paratos_exe = malloc(pathSize); 142 | if(paratos_exe == NULL) 143 | { 144 | fprintf(stderr, "malloc: Out of memory.\n"); 145 | exit(1); 146 | } 147 | #ifdef __APPLE__ 148 | if (_NSGetExecutablePath(paratos_exe, &pathSize) == 0) 149 | { 150 | break; // SUCCESS 151 | } 152 | else 153 | { 154 | free(paratos_exe); // _NSGetExecutablePath sets the path size to the size required 155 | } 156 | #else 157 | int r = readlink("/proc/self/exe", paratos_exe, pathSize); 158 | if (r == -1) 159 | { 160 | perror("readlink(\"/proc/self/exe\")"); 161 | exit(1); 162 | } 163 | else if (r < pathSize) 164 | { 165 | break; 166 | } 167 | else 168 | { 169 | free(paratos_exe); 170 | pathSize *= 2; 171 | } 172 | #endif 173 | } 174 | } 175 | 176 | static sigjmp_buf return_from_signal_handler; 177 | void handle_segv(int signum, siginfo_t * info, void * p) 178 | { 179 | printf("SIGSEGV Addr: %p (ST mem: %lx)\n", info->si_addr, info->si_addr-(void*)memory); 180 | siglongjmp(return_from_signal_handler, signum); 181 | } 182 | 183 | int main(int argc, char* argv[]) 184 | { 185 | if (argc < 2) 186 | { 187 | fprintf(stderr, "usage: %s [arguments...]\n", argv[0]); 188 | return 1; 189 | } 190 | 191 | FindExecutable(); 192 | InitM68KMemory(); 193 | InitMemory(); 194 | InitCookieJar(); 195 | 196 | current_process = LoadExe(argv[1], argv+1, argc-1); 197 | if (current_process == 0) 198 | { 199 | fprintf(stderr, "error: cannot load %s.\n", argv[1]); 200 | return 1; 201 | } 202 | 203 | m68k_init(); 204 | m68k_set_cpu_type(M68K_CPU_TYPE_68020); 205 | m68k_pulse_reset(); 206 | 207 | uint32_t hitpa = m68k_read_field(current_process, basepage_t, p_hitpa); 208 | uint32_t tbase = m68k_read_field(current_process, basepage_t, p_tbase); 209 | uint32_t stack = hitpa; 210 | 211 | stack-=4; 212 | m68k_write_memory_32(stack, current_process); 213 | stack-=4; 214 | m68k_write_memory_32(stack, 0); 215 | 216 | m68k_set_reg(M68K_REG_SP, current_process-4); 217 | m68k_set_reg(M68K_REG_SR, 0x0300); 218 | m68k_set_reg(M68K_REG_SP, stack); 219 | m68k_set_reg(M68K_REG_PC, tbase); 220 | 221 | struct sigaction act_segv; 222 | struct sigaction old_segv; 223 | act_segv.sa_sigaction = handle_segv; 224 | act_segv.sa_flags = SA_SIGINFO; 225 | sigaction(SIGSEGV, &act_segv, &old_segv); 226 | for (;;) 227 | { 228 | int ret = sigsetjmp(return_from_signal_handler, 1); 229 | if (!ret) 230 | { 231 | m68k_execute(10000); 232 | } 233 | else 234 | { 235 | static const char* regs[] = { 236 | "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", 237 | "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", 238 | "PC", "SR", "SP", "USP", "ISP", "MSP", "SFC", "DFC", "VBR", "CACR", "CAAR", 239 | "PREF_ADDR", "PREF_DATA", "PPC", "IR"}; 240 | printf("longjmp from exception handler returned %d\n", ret); 241 | // Handle return from signal handler 242 | if (ret == SIGSEGV) 243 | { 244 | sigaction(SIGSEGV, &old_segv, NULL); 245 | for(int i=M68K_REG_D0; i %08x\n", pc, m68k_get_reg(NULL, M68K_REG_PC)); 254 | emuptr32_t earlier = pc; 255 | for(int i=0; i<20; i++) 256 | { 257 | bool found = false; 258 | for (int j=24; j>0; j-=2) 259 | { 260 | if(j == m68k_disassemble(buf, earlier-j, M68K_CPU_TYPE_68020) ) 261 | { 262 | found = true; 263 | earlier -= j; 264 | break; 265 | } 266 | } 267 | if(!found) 268 | break; 269 | } 270 | int width = 0; 271 | for(emuptr32_t address = earlier ; address <= pc+32; address+=width) 272 | { 273 | width=m68k_disassemble(buf, address, M68K_CPU_TYPE_68020); 274 | printf("%s%08x %-32s", address==pc?"-> ":" ", address, buf); 275 | for(int j=0; j' '&&c<='~')?c:'.'); 285 | } 286 | printf("\n"); 287 | } 288 | exit(-1); 289 | // m68ki_exception_address_error(); 290 | } 291 | } 292 | } 293 | 294 | return 0; 295 | } 296 | -------------------------------------------------------------------------------- /gemdos/cdev.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "common.h" 5 | #include "tos_errors.h" 6 | 7 | /** 8 | * Cauxin - 3 9 | * 10 | * The GEMDOS routine Cauxin reads a character byte from the GEMDOS handle 2 11 | * - normally the serial port aux:. The function waits until the character 12 | * arrives. 13 | * 14 | * Note: Atari recommends use of the BIOS function Bconin for this, as Cauxin 15 | * can cause problems when its handle is redirected and end-of-file is 16 | * encountered. 17 | */ 18 | int32_t Cauxin ( void ) 19 | { 20 | NOT_IMPLEMENTED(GEMDOS, Cauxin, 3); 21 | return TOS_ENOSYS; 22 | } 23 | 24 | /** 25 | * Cauxis - 18 26 | * 27 | * The GEMDOS routine Cauxis checks the status of GEMDOS handle 2 - normally 28 | * the serial port aux: - to see whether at least one character is waiting to 29 | * be received. 30 | * 31 | * Note: The function works correctly with redirection of input/output only 32 | * as of GEMDOS Version 0.15. Atari recommends the use of the BIOS function 33 | * Bconstat instead. 34 | */ 35 | int16_t Cauxis ( void ) 36 | { 37 | NOT_IMPLEMENTED(GEMDOS, Cauxis, 18); 38 | return TOS_ENOSYS; 39 | } 40 | 41 | /** 42 | * Cauxos - 19 43 | * 44 | * The GEMDOS routine Cauxos checks the status of GEMDOS handle 2 - normally 45 | * the serial port aux: - to see whether it is ready to output characters. 46 | * 47 | * Note: Atari recommends use of the BIOS function Bcostat for this. 48 | */ 49 | int16_t Cauxos ( void ) 50 | { 51 | NOT_IMPLEMENTED(GEMDOS, Cauxos, 19); 52 | return TOS_ENOSYS; 53 | } 54 | 55 | /** 56 | * Cauxout - 4 57 | * 58 | * The GEMDOS routine Cauxout writes the character c to GEMDOS handle 2 - 59 | * normally the serial port aux:. 60 | * 61 | * Note: The function works correctly with redirection of input/output only 62 | * as of GEMDOS Version 0.15. Atari recommends the use of the BIOS function 63 | * Bconout instead. 64 | */ 65 | int32_t Cauxout ( int16_t c ) 66 | { 67 | NOT_IMPLEMENTED(GEMDOS, Cauxout, 4); 68 | return TOS_ENOSYS; 69 | } 70 | 71 | /** 72 | * Cconin - 1 73 | * 74 | * The GEMDOS routine Cconin reads a character from GEMDOS handle 0 - 75 | * normally the standard input device con: (the keyboard as a rule), waiting 76 | * until one is available. 77 | * 78 | * Note: By setting bit 3 of the system variable conterm one can have the 79 | * value of Kbshift returned in bits 24..31. Unfortunately there is no way to 80 | * recognise input/output redirection or end-of-file. That is why many 81 | * libraries define the key combination Control-Z (ASCII-code 26) as the 82 | * character for end-of-file. 83 | */ 84 | int32_t Cconin ( void ) 85 | { 86 | NOT_IMPLEMENTED(GEMDOS, Cconin, 1); 87 | return TOS_ENOSYS; 88 | } 89 | 90 | /** 91 | * Cconis - 11 92 | * 93 | * The GEMDOS routine Cconis checks whether a character is waiting to be read 94 | * in GEMDOS handle 0 - normally the standard input buffer of con:. 95 | */ 96 | int32_t Cconis ( void ) 97 | { 98 | NOT_IMPLEMENTED(GEMDOS, Cconis, 11); 99 | return TOS_ENOSYS; 100 | } 101 | 102 | /** 103 | * Cconos - 16 104 | * 105 | * The GEMDOS routine Cconos checks whether a character may be output to 106 | * GEMDOS handle 1 - normally the standard output device con:. 107 | * 108 | * Note: The function works correctly with redirection of input/output only 109 | * as of GEMDOS Version 0.15. 110 | */ 111 | int16_t Cconos ( void ) 112 | { 113 | NOT_IMPLEMENTED(GEMDOS, Cconos, 16); 114 | return TOS_ENOSYS; 115 | } 116 | 117 | /** 118 | * Cconout - 2 119 | * 120 | * The GEMDOS routine Cconout writes the character c to GEMDOS handle 1 - 121 | * normally the standard output device con: - as a rule the screen. 122 | * 123 | * Note: The character c contain the associated ASCII-code in bits 0..7 (all 124 | * other bits have to be 0). No line-feed translation is done at the output, 125 | * so ASCII 13 and ACII 10 must be sent to start a new line. The function 126 | * works correctly with redirection of the input/output only as of GEMDOS 127 | * Version 0.15; with earlier versions, do not redirect this handle to a 128 | * write-only device since the call tries to read from the output stream to 129 | * process the 'special' keys. 130 | */ 131 | int32_t Cconout ( int16_t c ) 132 | { 133 | return write(1, &c, 1); 134 | } 135 | 136 | /** 137 | * Cconrs - 10 138 | * 139 | * The GEMDOS routine Cconrs reads a string from the standard input stream 140 | * (GEMDOS handle 0) - normally the keyboard - and echoes it to the standard 141 | * output stream (GEMDOS handle 1) - normally the screen. 142 | * 143 | * To do this one writes in the component maxlen of LINE the number of bytes 144 | * to be read in - 1. The input will be terminated by the function when the 145 | * Return key is pressed or the maximum input length has been exceeded. 146 | * 147 | * 'Special' key combinations cause various actions. In detail: Input Meaning 148 | * 149 | * int32_t Cconrs ( LINE *buf ) 150 | */ 151 | int32_t Cconrs ( emuptr32_t buf ) 152 | { 153 | NOT_IMPLEMENTED(GEMDOS, Cconrs, 10); 154 | return TOS_ENOSYS; 155 | } 156 | 157 | /** 158 | * Cconws - 9 159 | * 160 | * The GEMDOS routine Cconws writes the string buf to GEMDOS handle 1 - 161 | * normally the standard output device con:. The string must be 162 | * NULL-terminated (with the ASCII character 0). With GEMDOS versions below 163 | * 0.15, do not redirect this handle to a write-only device since the call 164 | * tries to read from the output stream to process the 'special' keys. 165 | * 166 | * int32_t Cconws ( CONST BYTE *buf ) 167 | */ 168 | int32_t Cconws ( emuptr32_t address ) 169 | { 170 | uint8_t* buffer = &memory[address]; 171 | int count=strlen(buffer); 172 | int32_t bytes_written = write(1, buffer, count); 173 | if (bytes_written < 0) 174 | { 175 | return MapErrno(); 176 | } 177 | return TOS_E_OK; 178 | } 179 | 180 | /** 181 | * Cnecin - 8 182 | * 183 | * The GEMDOS routine Cnecin reads a character from GEMDOS handle 0 - 184 | * normally the standard input device con: (the keyboard as a rule), without 185 | * outputting it to the standard output device (normally the screen). 186 | * 187 | * Note: Flow control can be achieved with the key combinations Control-S 188 | * (stop output) or Control-Q (continue output). With GEMDOS versions prior 189 | * to 0.30, if the function's handle is redirected an end-of-file condition 190 | * will cause the system to hang. 191 | */ 192 | int32_t Cnecin ( void ) 193 | { 194 | NOT_IMPLEMENTED(GEMDOS, Cnecin, 8); 195 | return TOS_ENOSYS; 196 | } 197 | 198 | /** 199 | * Cprnos - 17 200 | * 201 | * The GEMDOS routine Cprnos checks whether handle 3 - normally the parallel 202 | * port prn: - is ready to accept characters. 203 | * 204 | * Note: It is strongly recommended to use this function before starting a 205 | * printout, since it takes around 30 seconds to recognize a printer timeout 206 | * if the printer is not ready (perhaps not switched on?). 207 | */ 208 | int16_t Cprnos ( void ) 209 | { 210 | NOT_IMPLEMENTED(GEMDOS, Cprnos, 17); 211 | return TOS_ENOSYS; 212 | } 213 | 214 | /** 215 | * Cprnout - 5 216 | * 217 | * The GEMDOS routine Cprnout writes the character c to GEMDOS handle 3 - 218 | * normally the printer port prn:. 219 | * 220 | * c is a WORD value, with the character to be output occupying bits 0..7; 221 | * all other bits must be 0. 222 | * 223 | * Note: Before print output one should check with Cprnos whether the printer 224 | * is ready. Settings made with the aid of the Setprt function are ignored 225 | * for printing by almost all TOS versions. With redirection of input/output, 226 | * the function works correctly only as of GEMDOS Version 0.15. 227 | */ 228 | int32_t Cprnout ( int16_t c ) 229 | { 230 | NOT_IMPLEMENTED(GEMDOS, Cprnout, 5); 231 | return TOS_ENOSYS; 232 | } 233 | 234 | /** 235 | * Crawcin - 7 236 | * 237 | * The GEMDOS routine Crawcin reads a character from GEMDOS handle 1 - 238 | * normally the standard input con: (the keyboard) - without echoing it 239 | * immediately to the screen or processing any of the 'special' keys. 240 | * 241 | * Note: The function works correctly with input/output redirection only as 242 | * of GEMDOS Version 0.15. 243 | */ 244 | int32_t Crawcin ( void ) 245 | { 246 | struct termios tp, save; 247 | int needRestore = 0; 248 | char input; 249 | 250 | /* Retrieve current terminal settings, turn echoing off */ 251 | 252 | if (tcgetattr(0, &tp) != -1) 253 | { 254 | save = tp; /* So we can restore settings later */ 255 | cfmakeraw(&tp); /* Switch termial to raw mode */ 256 | needRestore = (tcsetattr(0, TCSAFLUSH, &tp) != -1); 257 | } 258 | 259 | int retval = TOS_ENOSYS; 260 | 261 | if (read(0, &input, 1) == -1) 262 | { 263 | retval = MapErrno(); 264 | } 265 | else 266 | { 267 | retval = input; 268 | } 269 | if (needRestore) 270 | tcsetattr(STDIN_FILENO, TCSAFLUSH, &save); 271 | //TRACEF("Crawcin --> %08x %c\n", retval, retval); 272 | 273 | return retval; 274 | } 275 | 276 | /** 277 | * Crawio - 6 278 | * 279 | * The GEMDOS routine Crawio combines unbuffered console input and output in 280 | * one function. The following applies for the parameter w, of which only the 281 | * lower 8 bits are significant (others should be 0): Value Meaning 282 | */ 283 | int32_t Crawio ( int16_t w ) 284 | { 285 | if((w & 0xff) == 0xff) 286 | return Crawcin(); 287 | else 288 | return Cconout(w); 289 | } 290 | -------------------------------------------------------------------------------- /mushashi/readme.txt: -------------------------------------------------------------------------------- 1 | MUSASHI 2 | ======= 3 | 4 | Version 3.4 5 | 6 | A portable Motorola M680x0 processor emulation engine. 7 | Copyright 1998-2002 Karl Stenerud. All rights reserved. 8 | 9 | 10 | 11 | INTRODUCTION: 12 | ------------ 13 | 14 | Musashi is a Motorola 68000, 68010, 68EC020, and 68020 emulator written in C. 15 | This emulator was written with two goals in mind: portability and speed. 16 | 17 | The emulator is written to ANSI C89 specifications. It also uses inline 18 | functions, which are C9X compliant. 19 | 20 | It has been successfully running in the MAME project (www.mame.net) for years 21 | and so has had time to mature. 22 | 23 | 24 | 25 | LICENSE AND COPYRIGHT: 26 | --------------------- 27 | 28 | Copyright © 1998-2001 Karl Stenerud 29 | 30 | Permission is hereby granted, free of charge, to any person obtaining a copy 31 | of this software and associated documentation files (the "Software"), to deal 32 | in the Software without restriction, including without limitation the rights 33 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 34 | copies of the Software, and to permit persons to whom the Software is 35 | furnished to do so, subject to the following conditions: 36 | 37 | The above copyright notice and this permission notice shall be included in 38 | all copies or substantial portions of the Software. 39 | 40 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 41 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 42 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 43 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 44 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 45 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 46 | THE SOFTWARE. 47 | 48 | 49 | 50 | AVAILABILITY: 51 | ------------ 52 | The latest version of this code can be obtained at: 53 | https://github.com/kstenerud/Musashi 54 | 55 | 56 | 57 | CONTACTING THE AUTHOR: 58 | --------------------- 59 | I can be reached at kstenerud@gmail.com 60 | 61 | 62 | 63 | BASIC CONFIGURATION: 64 | ------------------- 65 | The basic configuration will give you a standard 68000 that has sufficient 66 | functionality to work in a primitive environment. 67 | 68 | This setup assumes that you only have 1 device interrupting it, that the 69 | device will always request an autovectored interrupt, and it will always clear 70 | the interrupt before the interrupt service routine finishes (but could 71 | possibly re-assert the interrupt). 72 | You will have only one address space, no tracing, and no instruction prefetch. 73 | 74 | To implement the basic configuration: 75 | 76 | - Open m68kconf.h and verify that the settings for INLINE will work with your 77 | compiler. (Currently set to "static __inline__", which works in gcc 2.9. 78 | For C9X compliance, it should be "inline") 79 | 80 | - In your host program, implement the following functions: 81 | unsigned int m68k_read_memory_8(unsigned int address); 82 | unsigned int m68k_read_memory_16(unsigned int address); 83 | unsigned int m68k_read_memory_32(unsigned int address); 84 | void m68k_write_memory_8(unsigned int address, unsigned int value); 85 | void m68k_write_memory_16(unsigned int address, unsigned int value); 86 | void m68k_write_memory_32(unsigned int address, unsigned int value); 87 | 88 | - In your host program, be sure to call m68k_pulse_reset() once before calling 89 | any of the other functions as this initializes the core. 90 | 91 | - Use m68k_execute() to execute instructions and m68k_set_irq() to cause an 92 | interrupt. 93 | 94 | 95 | 96 | ADDING PROPER INTERRUPT HANDLING: 97 | -------------------------------- 98 | The interrupt handling in the basic configuration doesn't emulate the 99 | interrupt acknowledge phase of the CPU and automatically clears an interrupt 100 | request during interrupt processing. 101 | While this works for most systems, you may need more accurate interrupt 102 | handling. 103 | 104 | To add proper interrupt handling: 105 | 106 | - In m68kconf.h, set M68K_EMULATE_INT_ACK to OPT_SPECIFY_HANDLER 107 | 108 | - In m68kconf.h, set M68K_INT_ACK_CALLBACK(A) to your interrupt acknowledge 109 | routine 110 | 111 | - Your interrupt acknowledge routine must return an interrupt vector, 112 | M68K_INT_ACK_AUTOVECTOR, or M68K_INT_ACK_SPURIOUS. most m68k 113 | implementations just use autovectored interrupts. 114 | 115 | - When the interrupting device is satisfied, you must call m68k_set_irq(0) to 116 | remove the interrupt request. 117 | 118 | 119 | 120 | MULTIPLE INTERRUPTS: 121 | ------------------- 122 | The above system will work if you have only one device interrupting the CPU, 123 | but if you have more than one device, you must do a bit more. 124 | 125 | To add multiple interrupts: 126 | 127 | - You must make an interrupt arbitration device that will take the highest 128 | priority interrupt and encode it onto the IRQ pins on the CPU. 129 | 130 | - The interrupt arbitration device should use m68k_set_irq() to set the 131 | highest pending interrupt, or 0 for no interrupts pending. 132 | 133 | 134 | 135 | SEPARATE IMMEDIATE READS: 136 | ------------------------ 137 | You can write faster memory access functions if you know whether you are 138 | fetching from ROM or RAM. Immediate reads are always from the program space 139 | (Always in ROM unless it is running self-modifying code). 140 | 141 | To enable separate immediate reads: 142 | 143 | - In m68kconf.h, turn on M68K_SEPARATE_READ_IMM. 144 | 145 | - In your host program, implement the following functions: 146 | unsigned int m68k_read_immediate_16(unsigned int address); 147 | unsigned int m68k_read_immediate_32(unsigned int address); 148 | 149 | - If you need to know the current PC (for banking and such), set 150 | M68K_MONITOR_PC to OPT_SPECIFY_HANDLER, and set M68K_SET_PC_CALLBACK(A) to 151 | your routine. 152 | 153 | 154 | 155 | ADDRESS SPACES: 156 | -------------- 157 | Most systems will only implement one address space, placing ROM at the lower 158 | addresses and RAM at the higher. However, there is the possibility that a 159 | system will implement ROM and RAM in the same address range, but in different 160 | address spaces. 161 | 162 | In this case, you might get away with assuming that immediate reads are in the 163 | program space and all other reads are in the data space, if it weren't for the 164 | fact that the exception vectors are fetched from the data space. As a result, 165 | anyone implementing this kind of system will have to copy the vector table 166 | from ROM to RAM using pc-relative instructions. 167 | 168 | This makes things bad for emulation, because this means that a non-immediate 169 | read is not necessarily in the data space. 170 | The m68k deals with this by encoding the requested address space on the 171 | function code pins: 172 | 173 | FC 174 | Address Space 210 175 | ------------------ --- 176 | USER DATA 001 177 | USER PROGRAM 010 178 | SUPERVISOR DATA 101 179 | SUPERVISOR PROGRAM 110 180 | CPU SPACE 111 <-- not emulated in this core since we emulate 181 | interrupt acknowledge in another way. 182 | 183 | To emulate the function code pins: 184 | 185 | - In m68kconf.h, set M68K_EMULATE_FC to OPT_SPECIFY_HANDLER and set 186 | M68K_SET_FC_CALLBACK(A) to your function code handler function. 187 | 188 | - Your function code handler should select the proper address space for 189 | subsequent calls to m68k_read_xx (and m68k_write_xx for 68010+). 190 | 191 | Note: immediate reads are always done from program space, so technically you 192 | don't need to implement the separate immediate reads, although you could 193 | gain more speed improvements leaving them in and doing some clever 194 | programming. 195 | 196 | 197 | 198 | USING DIFFERENT CPU TYPES: 199 | ------------------------- 200 | The default is to enable only the 68000 cpu type. To change this, change the 201 | settings for M68K_EMULATE_010 etc in m68kconf.h. 202 | 203 | To set the CPU type you want to use: 204 | 205 | - Make sure it is enabled in m68kconf.h. Current switches are: 206 | M68K_EMULATE_010 207 | M68K_EMULATE_EC020 208 | M68K_EMULATE_020 209 | 210 | - In your host program, call m68k_set_cpu_type() and then call 211 | m68k_pulse_reset(). Valid CPU types are: 212 | M68K_CPU_TYPE_68000, 213 | M68K_CPU_TYPE_68010, 214 | M68K_CPU_TYPE_68EC020, 215 | M68K_CPU_TYPE_68020 216 | 217 | 218 | 219 | CLOCK FREQUENCY: 220 | --------------- 221 | In order to emulate the correct clock frequency, you will have to calculate 222 | how long it takes the emulation to execute a certain number of "cycles" and 223 | vary your calls to m68k_execute() accordingly. 224 | As well, it is a good idea to take away the CPU's timeslice when it writes to 225 | a memory-mapped port in order to give the device it wrote to a chance to 226 | react. 227 | 228 | You can use the functions m68k_cycles_run(), m68k_cycles_remaining(), 229 | m68k_modify_timeslice(), and m68k_end_timeslice() to do this. 230 | Try to use large cycle values in your calls to m68k_execute() since it will 231 | increase throughput. You can always take away the timeslice later. 232 | 233 | 234 | 235 | MORE CORRECT EMULATION: 236 | ---------------------- 237 | You may need to enable these in order to properly emulate some of the more 238 | obscure functions of the m68k: 239 | 240 | - M68K_EMULATE_BKPT_ACK causes the CPU to call a breakpoint handler on a BKPT 241 | instruction 242 | 243 | - M68K_EMULATE_TRACE causes the CPU to generate trace exceptions when the 244 | trace bits are set 245 | 246 | - M68K_EMULATE_RESET causes the CPU to call a reset handler on a RESET 247 | instruction. 248 | 249 | - M68K_EMULATE_PREFETCH emulates the 4-word instruction prefetch that is part 250 | of the 68000/68010 (needed for Amiga emulation). 251 | NOTE: if the CPU fetches a word or longword at an odd address when this 252 | option is on, it will yield unpredictable results, which is why a real 253 | 68000 will generate an address error exception. 254 | 255 | - M68K_EMULATE_ADDRESS_ERROR will cause the CPU to generate address error 256 | exceptions if it attempts to read a word or longword at an odd address. 257 | 258 | - call m68k_pulse_halt() to emulate the HALT pin. 259 | 260 | 261 | 262 | CONVENIENCE FUNCTIONS: 263 | --------------------- 264 | These are in here for programmer convenience: 265 | 266 | - M68K_INSTRUCTION_HOOK lets you call a handler before each instruction. 267 | 268 | - M68K_LOG_ENABLE and M68K_LOG_1010_1111 lets you log illegal and A/F-line 269 | instructions. 270 | 271 | 272 | 273 | MULTIPLE CPU EMULATION: 274 | ---------------------- 275 | The default is to use only one CPU. To use more than one CPU in this core, 276 | there are some things to keep in mind: 277 | 278 | - To have different cpus call different functions, use OPT_ON instead of 279 | OPT_SPECIFY_HANDLER, and use the m68k_set_xxx_callback() functions to set 280 | your callback handlers on a per-cpu basis. 281 | 282 | - Be sure to call set_cpu_type() for each CPU you use. 283 | 284 | - Use m68k_set_context() and m68k_get_context() to switch to another CPU. 285 | 286 | 287 | 288 | LOAD AND SAVE CPU CONTEXTS FROM DISK: 289 | ------------------------------------ 290 | You can use them68k_load_context() and m68k_save_context() functions to load 291 | and save the CPU state to disk. 292 | 293 | 294 | 295 | GET/SET INFORMATION FROM THE CPU: 296 | -------------------------------- 297 | You can use m68k_get_reg() and m68k_set_reg() to gain access to the internals 298 | of the CPU. 299 | 300 | 301 | 302 | EXAMPLE: 303 | ------- 304 | 305 | The subdir example contains a full example (currently DOS only). 306 | -------------------------------------------------------------------------------- /gemdos/sys.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "common.h" 9 | #include "tos_errors.h" 10 | #include "sysvars.h" 11 | #include "cookiejar.h" 12 | #include 13 | #ifdef LINUX 14 | #include 15 | #include 16 | #include 17 | #endif 18 | 19 | 20 | #include "m68k.h" 21 | #include "m68kcpu.h" 22 | 23 | /** 24 | * Salert - 316 25 | * 26 | * The function Salert outputs an alert or error-message that is written to 27 | * the alert pipeline U:\PIPE\ALERT. 28 | * 29 | * The message msg should not contain any escape or control characters, 30 | * linefeeds etc. It should be a simple one-line NULL-terminated character 31 | * string alert- or error-message. 32 | * 33 | * The function formats the message itself and sends it to the user. The 34 | * exact format of the output however depends on the system configuration in 35 | * use. 36 | * 37 | * void Salert ( int8_t *msg ) 38 | */ 39 | void Salert ( emuptr32_t msg ) 40 | { 41 | NOT_IMPLEMENTED(GEMDOS, Salert, 316); 42 | } 43 | 44 | /** 45 | * Sconfig - 51 46 | * 47 | * The function Sconfig permits the configuration of parts of the operating 48 | * system. The following apply: Parameter Meaning 49 | */ 50 | int32_t Sconfig ( int16_t mode, int32_t flags ) 51 | { 52 | NOT_IMPLEMENTED(GEMDOS, Sconfig, 51); 53 | return TOS_ENOSYS; 54 | } 55 | 56 | /** 57 | * Shutdown - 337 58 | * 59 | * The function Shutdown kills all processes, syncs file-systems then halts 60 | * or reboots the system. 61 | * 62 | * On mode equal to SHUT_HALT (0L), the system will shut down, then enter a 63 | * halted condition. 64 | * 65 | * On mode equal to SHUT_BOOT (1L), the system will reboot the machine after 66 | * shutting everything down. 67 | * 68 | * On mode equal to SHUT_COLD (2L), the system will act the same as with the 69 | * SHUT_BOOT mode, except that a cold start rather than the warm start will 70 | * be performed. SHUT_COLD mode is recognized as of FreeMiNT version 1.15.5; 71 | * older versions of the kernel will treat the SHUT_COLD mode as SHUT_BOOT. 72 | * 73 | * On mode equal to SHUT_POWER (3L), the system will turn the power off. The 74 | * only hardware that supports it at present is CT60. If the hardware does 75 | * not support it, SHUT_HALT will be performed. SHUT_POWER mode is recognized 76 | * as of FreeMiNT version 1.16a; older versions of the kernel will treat the 77 | * SHUT_POWER mode as SHUT_COLD. 78 | * 79 | * All other values of mode are reserved for future definition. 80 | * 81 | * Older versions of MiNT contained a bug that might cause the system to 82 | * crash if you called Shutdown while both GEM AES and virtual console daemon 83 | * were present. 84 | * 85 | * You need root privileges to shut the system down. 86 | */ 87 | void Shutdown ( int32_t mode ) 88 | { 89 | NOT_IMPLEMENTED(GEMDOS, Shutdown, 337); 90 | } 91 | 92 | /** 93 | * Slbclose - 23 94 | * 95 | * The function Slbclose closes a shared library. 96 | * 97 | * The parameter sl is the descriptor that is obtained with Slbclose. 98 | * 99 | * int32_t Slbclose( SHARED_LIB *sl ) 100 | */ 101 | int32_t Slbclose( emuptr32_t sl ) 102 | { 103 | NOT_IMPLEMENTED(GEMDOS, Slbclose, 23); 104 | return TOS_ENOSYS; 105 | } 106 | 107 | /** 108 | * Slbopen - 22 109 | * 110 | * The function Slbopen opens a shared library. Parameter Meaning 111 | * 112 | * int32_t Slbopen( int8_t *name, int8_t *path, int32_t min_ver, SHARED_LIB *sl, SLB_EXEC *fn ) 113 | */ 114 | int32_t Slbopen( emuptr32_t name, emuptr32_t path, int32_t min_ver, emuptr32_t sl, emuptr32_t fn ) 115 | { 116 | NOT_IMPLEMENTED(GEMDOS, Slbopen, 22); 117 | return TOS_ENOSYS; 118 | } 119 | 120 | /** 121 | * Srealloc - 21 122 | * 123 | * The GEMDOS routine Srealloc allocates a block of length len bytes for the 124 | * screen memory. 125 | * 126 | * The screen memory is a block of the ST-RAM whose owner is the boot 127 | * process. The address of the screen memory (logbase or physbase) is not 128 | * affected by this function. 129 | */ 130 | int32_t Srealloc ( int32_t len ) 131 | { 132 | NOT_IMPLEMENTED(GEMDOS, Srealloc, 21); 133 | return TOS_ENOSYS; 134 | } 135 | 136 | /** 137 | * Ssync - 336 138 | * 139 | * The function Ssync synchronises all mounted file-systems. 140 | * 141 | * Warning: In MiNT this function is designated as Sync. 142 | */ 143 | void Ssync ( void ) 144 | { 145 | sync(); 146 | } 147 | 148 | enum ssystem_mode 149 | { 150 | S_OSNAME = 0, 151 | S_OSXNAME = 1, 152 | S_OSVERSION = 2, 153 | S_OSHEADER = 3, 154 | S_OSBUILDDATE = 4, 155 | S_OSBUILDTIME = 5, 156 | S_OSCOMPILE = 6, 157 | S_OSFEATURES = 7, 158 | S_GETCOOKIE = 8, 159 | S_SETCOOKIE = 9, 160 | S_GETLVAL = 10, 161 | S_GETWVAL = 11, 162 | S_GETBVAL = 12, 163 | S_SETLVAL = 13, 164 | S_SETWVAL = 14, 165 | S_SETBVAL = 15, 166 | S_SECLEVEL = 16, 167 | S_RUNLEVEL = 17, /* currently disabled, reserved */ 168 | S_TSLICE = 18, 169 | S_FASTLOAD = 19, 170 | S_SYNCTIME = 20, 171 | S_BLOCKCACHE = 21, 172 | S_FLUSHCACHE = 22, 173 | S_CTRLCACHE = 23, 174 | S_INITIALTPA = 24, 175 | S_CTRLALTDEL = 25, /* ctraltdel behavoiur */ 176 | S_DELCOOKIE = 26, 177 | S_LOADKBD = 27, /* reload the keyboard table */ 178 | S_CLOCKUTC = 100, 179 | S_TIOCMGET = 0x54f8, /* 21752 */ 180 | 181 | /* experimental - need feedback 182 | * additional informations about the kernel 183 | * reserved 900 - 999 184 | */ 185 | S_KNAME = 900, /* kernel name - arg1 pointer to a buffer of arg2 len */ 186 | S_CNAME = 910, /* compiler name - arg1 pointer to a buffer of arg2 len */ 187 | S_CVERSION = 911, /* compiler version - arg1 pointer to a buffer of arg2 len */ 188 | S_CDEFINES = 912, /* compiler definitions - arg1 pointer to a buffer of arg2 len */ 189 | S_COPTIM = 913, /* compiler flags - arg1 pointer to a buffer of arg2 len */ 190 | 191 | /* debug section 192 | * reserved 1000 - 1999 193 | */ 194 | S_DEBUGLEVEL = 1000, /* debug level */ 195 | S_DEBUGDEVICE = 1001, /* BIOS device number */ 196 | S_DEBUGKMTRACE = 1100, /* KM_TRACE debug feature */ 197 | 198 | }; 199 | /** 200 | * Ssystem - 340 201 | * 202 | * The Ssystem call has been designed to make your life easier. Using this 203 | * you can get some closer control of the system and the kernel itself. Via 204 | * this call the kernel now supports e.g. an easy cookie jar management and 205 | * provides a safe access to supervisor memory. It's strictly encouraged to 206 | * access GEMDOS variables and system vectors via the Ssystem, because this 207 | * way is considered safe for multi-user setups. 208 | * 209 | * arg1 and arg2 are LONG parameters specific to a particular mode. If a mode 210 | * doesn't use a parameter, it is usually ignored, but should be set to zero 211 | * for future compatibility. mode specifies a particular action as follows: 212 | * mode Meaning 213 | */ 214 | int32_t Ssystem(uint16_t mode, int32_t arg1, int32_t arg2) 215 | { 216 | //TRACEF("SSystem(0x%04x, %x, %x)\n", mode, arg1, arg2); 217 | switch(mode) 218 | { 219 | case 0xffff: 220 | return 0; 221 | case S_OSNAME: 222 | return 0x70544f53; // 'pTOS' 223 | case S_OSXNAME: 224 | return 0; 225 | case S_OSVERSION: 226 | return 0x00000061; // 0.0.0a 227 | case S_OSHEADER: 228 | switch (arg1) 229 | { 230 | case 0: 231 | return 0; 232 | case 2: 233 | return 0x0400; 234 | default: 235 | return TOS_EINVAL; 236 | } 237 | case S_OSCOMPILE: 238 | return 0x0014; // 68020 239 | case S_OSFEATURES: 240 | return 0x3; // memory protection + virtual memory 241 | case S_GETCOOKIE: 242 | { 243 | uint32_t value; 244 | int found = ReadCookie(arg1, &value); 245 | if (arg2) 246 | { 247 | m68k_write_memory_32(arg2, value); 248 | return found; 249 | } 250 | return value; 251 | } 252 | case S_SETCOOKIE: 253 | return TOS_EACCDN; // Changing cookies from client code does currently not make sense in paratos 254 | case S_GETLVAL: 255 | switch (arg1) 256 | { 257 | case _hz_200: 258 | { 259 | struct timeval tv; 260 | if(gettimeofday(&tv, NULL) != 0) 261 | return 0; 262 | 263 | return (tv.tv_sec * 200) + (tv.tv_usec / 5000); 264 | } 265 | default: 266 | { 267 | return m68k_read_memory_32((uint32_t)(arg1&0xffff)); 268 | } 269 | } 270 | case S_GETWVAL: 271 | return m68k_read_memory_16((uint32_t)(arg1&0xffff)); 272 | case S_GETBVAL: 273 | return m68k_read_memory_8((uint32_t)(arg1&0xffff)); 274 | case S_SETLVAL: 275 | case S_SETWVAL: 276 | case S_SETBVAL: 277 | case S_SECLEVEL: 278 | return TOS_EACCDN; 279 | default: 280 | NOT_IMPLEMENTED(GEMDOS, Ssystem_mode, mode); 281 | return TOS_ENOSYS; 282 | } 283 | } 284 | 285 | /** 286 | * Super - 32 287 | * 288 | * The GEMDOS routine Super switches between user- and supervisor-mode, or 289 | * interrogates the current operating 290 | * 291 | * The address of the supervisor stack may be altered during switching. If 292 | * one passes the value 1 for the parameter stack, then the current mode will 293 | * be interrogated. The value 0 switches between user- and supervisor-mode. 294 | * All other values will be regarded as the new address for the supervisor 295 | * stack. In the supervisor-mode one can access all protected memory blocks. 296 | * 297 | * Note: With regard to future operating system versions, you should avoid 298 | * use of the Super function. This function gives rise to major problems in 299 | * conjunction with virtual memory and interruptible (and re-entrant) 300 | * file-systems in a multitasking system. If neccesary, alter your code so 301 | * that you can use the BIOS function Supexec. 302 | * 303 | * For MiNT the following applies: 304 | * If you are in SECURELEVEL > 1, you must have root privileges to get into 305 | * supervisor-mode. If you don't have them and call Super, the SIGSYS signal 306 | * (12) will be raised. 307 | * 308 | * int32_t Super ( void *stack ) 309 | */ 310 | int32_t Super ( emuptr32_t stack ) 311 | { 312 | // Emulating Mint SECURELEVEL>1 behavior for a non-root process: 313 | kill(getpid(), SIGSYS); 314 | return TOS_EACCDN; 315 | } 316 | 317 | /** 318 | * Suptime - 319 319 | * 320 | * The function Suptime returns the current uptime and load averages from the 321 | * system. 322 | * 323 | * uptime is a pointer to a LONG value that will be filled with the current 324 | * amount of seconds elapsed since the last reboot. 325 | * 326 | * loadaverage is a pointer to a three LONGword array, that will be filled 327 | * with the current load averages (for the last 1, 5 and 15 minutes). 328 | * 329 | * The load average value is calculated using the following formula: 330 | * 331 | * sum += (new_load - old_load) * LOAD_SCALE; 332 | * load_avg = sum / MAX_SIZE; 333 | * 334 | * where LOAD_SCALE is 2048, MAX_SIZE is the number of 5 second periods in 335 | * the specified amount of time (12 for 1 min., 60 for 5 min. and 180 for 15 336 | * min). new_load is the number of currently running processes, old_load is 337 | * the number of processes running previous time. A 'running' process is 338 | * considered the one which is put onto run or ready queue. 339 | * 340 | * int32_t Suptime ( int32_t *uptime, int32_t *loadaverage ) 341 | */ 342 | int32_t Suptime ( emuptr32_t uptime, emuptr32_t loadaverage ) 343 | { 344 | #ifdef LINUX 345 | struct sysinfo info; 346 | sysinfo(&info); 347 | m68k_write_memory_32(uptime, info.uptime); 348 | m68k_write_memory_32(loadaverage, (int32_t)(info.loads[0]*2048)); 349 | m68k_write_memory_32(loadaverage+1, (int32_t)(info.loads[1]*2048)); 350 | m68k_write_memory_32(loadaverage+2, (int32_t)(info.loads[2]*2048)); 351 | 352 | return TOS_E_OK; 353 | #else 354 | NOT_IMPLEMENTED(GEMDOS, Suptime, 319); 355 | return TOS_ENOSYS; 356 | #endif 357 | } 358 | 359 | /** 360 | * Sversion - 48 361 | * 362 | * The GEMDOS routine Sversion obtains the version number of the GEMDOS. At 363 | * the present time, the following versions are known: Version Explanation 364 | */ 365 | uint16_t Sversion ( void ) 366 | { 367 | NOT_IMPLEMENTED(GEMDOS, Sversion, 48); 368 | return TOS_ENOSYS; 369 | } 370 | 371 | /** 372 | * Syield - 255 373 | * 374 | * By calling the Syield function a process releases the CPU, so that it can 375 | * be allocated to other processes (possibly earlier than planned). 376 | * 377 | * Note: Under MagiC this function directly calls the AES routine appl_yield, 378 | * and will be ignored in the Auto folder. 379 | */ 380 | void Syield ( void ) 381 | { 382 | NOT_IMPLEMENTED(GEMDOS, Syield, 255); 383 | } 384 | 385 | /** 386 | * Sysconf - 290 387 | * 388 | * The function Sysconf returns information about the capabilities (or the 389 | * configuration) of the operating system. With the parameter n various 390 | * values can be interrogated: n Meaning 391 | */ 392 | int32_t Sysconf ( int16_t n ) 393 | { 394 | NOT_IMPLEMENTED(GEMDOS, Sysconf, 290); 395 | return TOS_ENOSYS; 396 | } 397 | -------------------------------------------------------------------------------- /gemdos/gemdos.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | // Character input/output 4 | 5 | int32_t Cauxin ( void ); 6 | int16_t Cauxis ( void ); 7 | int16_t Cauxos ( void ); 8 | int32_t Cauxout ( int16_t c ); 9 | int32_t Cconin ( void ); 10 | int32_t Cconis ( void ); 11 | int16_t Cconos ( void ); 12 | int32_t Cconout ( int16_t c ); 13 | int32_t Cconrs ( emuptr32_t buf ); 14 | int32_t Cconws ( emuptr32_t buf ); 15 | int32_t Cnecin ( void ); 16 | int16_t Cprnos ( void ); 17 | int32_t Cprnout ( int16_t c ); 18 | int32_t Crawcin ( void ); 19 | int32_t Crawio ( int16_t w ); 20 | 21 | // Date and Time 22 | 23 | int32_t Talarm ( int32_t time ); 24 | uint32_t Tgetdate ( void ); 25 | uint32_t Tgettime ( void ); 26 | int32_t Tgettimeofday ( emuptr32_t tv, emuptr32_t tzp ); 27 | int32_t Tmalarm( int32_t time ); 28 | int16_t Tsetdate ( uint16_t date ); 29 | int32_t Tsetitimer ( int16_t which, emuptr32_t interval, emuptr32_t value, emuptr32_t ointerval, emuptr32_t ovalue ); 30 | int16_t Tsettime ( uint16_t time ); 31 | int32_t Tsettimeofday ( emuptr32_t tv, emuptr32_t tzp ); 32 | 33 | // Directory functions 34 | 35 | int32_t Dchroot( emuptr32_t path ); 36 | int32_t Dclosedir ( int32_t dirhandle ); 37 | int32_t Dcntl ( int16_t cmd, emuptr32_t name, int32_t arg ); 38 | int32_t Dcreate ( emuptr32_t path ); 39 | int32_t Ddelete ( emuptr32_t path ); 40 | int16_t Dfree ( emuptr32_t buf, int16_t driveno ); 41 | int32_t Dgetcwd ( emuptr32_t path, int16_t drv, int16_t size ); 42 | int16_t Dgetdrv ( void ); 43 | int16_t Dgetpath ( emuptr32_t path, int16_t driveno ); 44 | int32_t Dlock ( int16_t mode, int16_t drv ); 45 | int32_t Dopendir ( emuptr32_t name, int16_t flag ); 46 | int32_t Dpathconf ( emuptr32_t name, int16_t mode ); 47 | int32_t Dreaddir ( int16_t len, int32_t dirhandle, emuptr32_t buf ); 48 | int32_t Dreadlabel ( emuptr32_t path, emuptr32_t label, int16_t length ); 49 | int32_t Drewinddir ( int32_t handle ); 50 | int32_t Dsetdrv ( int16_t drv ); 51 | int16_t Dsetpath ( emuptr32_t path ); 52 | int32_t Dwritelabel ( emuptr32_t path, emuptr32_t label ); 53 | int32_t Dxopendir ( emuptr32_t name, int16_t flag ); 54 | int32_t Dxreaddir ( int16_t ln, int32_t dirh, emuptr32_t buf, emuptr32_t xattr, emuptr32_t xr ); 55 | 56 | // File functions 57 | 58 | int16_t Fattrib ( emuptr32_t filename, int16_t wflag, int16_t attrib ); 59 | int32_t Fchmod ( emuptr32_t name, int16_t mode ); 60 | int32_t Fchown ( emuptr32_t name, int16_t uid, int16_t gid ); 61 | int16_t Fclose ( int16_t handle ); 62 | int32_t Fcntl ( int16_t fh, int32_t arg, int16_t cmd ); 63 | int16_t Fcreate ( emuptr32_t fname, int16_t attr ); 64 | void Fdatime ( emuptr32_t timeptr, int16_t handle, int16_t wflag ); 65 | int16_t Fdelete ( emuptr32_t fname ); 66 | int16_t Fdup ( int16_t handle ); 67 | int32_t Ffchmod ( int16_t fd, int16_t mode); 68 | int32_t Ffchown ( int16_t fd, int16_t uid, int16_t gid ); 69 | int16_t Fforce ( int16_t stdh, int16_t nonstdh ); 70 | int16_t Ffstat64 (int16_t fd, /*struct stat */ uint32_t address); 71 | int32_t Fgetchar ( int16_t fh, int16_t mode ); 72 | emuptr32_t Fgetdta ( void ); 73 | int32_t Finstat ( int16_t fh ); 74 | int32_t Flink ( emuptr32_t oldname, emuptr32_t newname ); 75 | int32_t Flock ( int16_t handle, int16_t mode, int32_t start, int32_t length ); 76 | int32_t Fmidipipe ( int16_t pid, int16_t in, int16_t out ); 77 | int32_t Fopen ( emuptr32_t fname, int16_t mode ); 78 | int32_t Foutstat ( int16_t fh ); 79 | int16_t Fpipe ( emuptr32_t usrh ); 80 | int32_t Fpoll ( /*POLLFD */ emuptr32_t fds, uint32_t nfds, uint32_t timeout ); 81 | int32_t Fputchar ( int16_t fh, int32_t ch, int16_t mode ); 82 | int32_t Fread ( int16_t handle, int32_t count, emuptr32_t buf ); 83 | int32_t Freadlink ( int16_t bufsiz, emuptr32_t buf, emuptr32_t name ); 84 | int32_t Frename ( emuptr32_t oldname, emuptr32_t newname ); 85 | int32_t Fseek ( int32_t offset, int16_t handle, int16_t seekmode ); 86 | int32_t Fselect ( uint16_t timeout, emuptr32_t rfds, emuptr32_t wfds, emuptr32_t efds ); 87 | void Fsetdta ( emuptr32_t buf ); 88 | int32_t Fsfirst ( emuptr32_t filename, int16_t attr ); 89 | int16_t Fsnext ( void ); 90 | int16_t Fstat64 ( uint16_t flag, uint32_t name, /*struct stat */ uint32_t address ); 91 | int32_t Fsymlink ( emuptr32_t oldname, emuptr32_t newname ); 92 | int32_t Fwrite ( int16_t handle, int32_t count, emuptr32_t buf ); 93 | int32_t Fxattr ( int16_t flag, emuptr32_t name, emuptr32_t xattr ); 94 | int32_t _Ffxattr ( int16_t handle, emuptr32_t xattr ); 95 | 96 | // Memory management 97 | 98 | int32_t Maddalt ( emuptr32_t start, int32_t size ); 99 | emuptr32_t Malloc ( int32_t number ); 100 | int32_t Mfree ( emuptr32_t block ); 101 | int32_t Mshrink ( emuptr32_t block, int32_t newsiz ); 102 | emuptr32_t Mxalloc ( int32_t amount, int16_t mode ); 103 | 104 | // Network functions 105 | 106 | int32_t Nversion( void ); 107 | 108 | // Process functions 109 | 110 | void Pause ( void ); 111 | int16_t Pdomain ( int16_t dom ); 112 | int32_t Pexec ( uint16_t mode, emuptr32_t arg1, emuptr32_t arg2, emuptr32_t env ); 113 | int16_t Pfork ( void ); 114 | int16_t Pgetauid ( void ); 115 | int32_t Pgetegid ( void ); 116 | int32_t Pgeteuid ( void ); 117 | int16_t Pgetgid ( void ); 118 | int32_t Pgetgroups( int16_t len, emuptr32_t gidset); 119 | int16_t Pgetpgrp ( void ); 120 | int16_t Pgetpid ( void ); 121 | int16_t Pgetppid ( void ); 122 | int32_t Pgetpriority ( int16_t which, int16_t who); 123 | int16_t Pgetuid ( void ); 124 | int16_t Pkill ( int16_t pid, int16_t sig ); 125 | int32_t Pmsg ( int16_t mode, int32_t mbox, emuptr32_t msg ); 126 | int16_t Pnice ( int16_t delta ); 127 | int32_t Prenice ( int16_t pid, int16_t delta ); 128 | void Prusage ( emuptr32_t r ); 129 | int32_t Psemaphore ( int16_t mode, int32_t id, int32_t timeout ); 130 | int16_t Psetauid ( int16_t id ); 131 | int32_t Psetegid ( int16_t egid ); 132 | int32_t Pseteuid ( int16_t euid ); 133 | int16_t Psetgid ( int16_t id ); 134 | int32_t Psetgroups( int16_t len, emuptr32_t gidset); 135 | int32_t Psetlimit ( int16_t lim, int32_t value ); 136 | int16_t Psetpgrp ( int16_t pid, int16_t newgrp ); 137 | int32_t Psetpriority ( int16_t which, int16_t who, int16_t pri ); 138 | int32_t Psetregid ( int16_t rgid, int16_t egid); 139 | int32_t Psetreuid ( int16_t ruid, int16_t euid); 140 | int16_t Psetuid ( int16_t id ); 141 | int32_t Psigaction ( int16_t sig, emuptr32_t act, emuptr32_t oact ); 142 | int32_t Psigblock ( int32_t mask ); 143 | int32_t Psigintr ( int16_t vec, int16_t sig ); 144 | int32_t Psignal ( int16_t sig, int32_t handler ); 145 | int32_t Psigpause ( int32_t mask ); 146 | int32_t Psigpending ( void ); 147 | void Psigreturn ( void ); 148 | int32_t Psigsetmask ( int32_t mask ); 149 | void Pterm ( uint16_t retcode ); 150 | void Pterm0 ( void ); 151 | void Ptermres ( int32_t keepcnt, int16_t retcode ); 152 | int16_t Ptrace(int16_t request, int16_t pid, emuptr32_t addr, int32_t data); 153 | int16_t Pumask ( int16_t mode ); 154 | int32_t Pusrval ( int32_t val ); 155 | int16_t Pvfork ( void ); 156 | int32_t Pwait ( void ); 157 | int32_t Pwait3 ( int16_t flag, emuptr32_t rusage ); 158 | int32_t Pwaitpid ( int16_t pid, int16_t flag, emuptr32_t rusage ); 159 | 160 | // System functions 161 | 162 | void Salert ( emuptr32_t msg ); 163 | int32_t Sconfig ( int16_t mode, int32_t flags ); 164 | void Shutdown ( int32_t mode ); 165 | int32_t Slbclose( emuptr32_t sl ); 166 | int32_t Slbopen( emuptr32_t name, emuptr32_t path, int32_t min_ver, emuptr32_t sl, emuptr32_t fn ); 167 | int32_t Srealloc ( int32_t len ); 168 | void Ssync ( void ); 169 | int32_t Ssystem ( int16_t mode, int32_t arg1, int32_t arg2 ); 170 | int32_t Super ( emuptr32_t stack ); 171 | int32_t Suptime ( emuptr32_t uptime, emuptr32_t loadaverage ); 172 | uint16_t Sversion ( void ); 173 | void Syield ( void ); 174 | int32_t Sysconf ( int16_t n ); 175 | 176 | uint32_t unixtime2dos(const time_t* unixtime); 177 | time_t dostime2unix(uint32_t dostime); 178 | 179 | void InitMemory(); 180 | 181 | 182 | // Data types 183 | struct DTA 184 | { 185 | int8_t d_reserved[21]; /* Reserved for GEMDOS */ 186 | uint8_t d_attrib; /* File attributes */ 187 | uint16_t d_time; /* Time */ 188 | uint16_t d_date; /* Date */ 189 | int32_t d_length; /* File length */ 190 | int8_t d_fname[14]; /* Filename */ 191 | } __attribute__((packed))__ ; 192 | 193 | typedef struct 194 | { 195 | uint32_t p_lowtpa; /* Start address of the TPA */ 196 | uint32_t p_hitpa; /* First byte after the end of the TPA */ 197 | uint32_t p_tbase; /* Start address of the program code */ 198 | int32_t p_tlen; /* Length of the program code */ 199 | uint32_t p_dbase; /* Start address of the DATA segment */ 200 | int32_t p_dlen; /* Length of the DATA section */ 201 | uint32_t p_bbase; /* Start address of the BSS segment */ 202 | int32_t p_blen; /* Length of the BSS section */ 203 | uint32_t p_dta; /* Pointer to the DTA */ 204 | /* Warning: Points first to the */ 205 | /* command line ! */ 206 | uint32_t p_parent; /* Pointer to the basepage of the */ 207 | /* calling processes */ 208 | int32_t p_resrvd0; /* Reserved */ 209 | uint32_t p_env; /* Address of the environment string */ 210 | int8_t p_resrvd1[80]; /* Reserved */ 211 | int8_t p_cmdlin[128]; /* Command line */ 212 | // the following are non-standard extensions 213 | int32_t mint_domain; 214 | } __attribute__((packed)) basepage_t; 215 | 216 | typedef struct xattr 217 | { 218 | uint16_t mode; /* File mode, statements for */ 219 | /* - possible filetypes */ 220 | /* - special bits */ 221 | /* - access rights */ 222 | int32_t index; /* File number */ 223 | uint16_t dev; /* Device number */ 224 | uint16_t rdev; /* Actual device */ 225 | /* (e.g. for BIOS files) */ 226 | /* in MagiC however reserved */ 227 | uint16_t nlink; /* Number of links */ 228 | uint16_t uid; /* User number */ 229 | uint16_t gid; /* Group number */ 230 | int32_t size; /* File length */ 231 | int32_t blksize, nblocks; /* Blocksize/occupied blocks */ 232 | union { 233 | struct { 234 | uint16_t mtime, mdate; /* Date of last modification */ 235 | }__attribute__((packed)); 236 | uint32_t mdostime; 237 | }; 238 | union { 239 | struct { 240 | uint16_t atime, adate; /* Date of last access */ 241 | }__attribute__((packed)); 242 | uint32_t adostime; 243 | }; 244 | union { 245 | struct { 246 | uint16_t ctime, cdate; /* Creation date */ 247 | }__attribute__((packed)); 248 | uint32_t cdostime; 249 | }; 250 | uint16_t attr; /* TOS file attributes */ 251 | uint16_t reserved2; /* Reserved */ 252 | uint64_t reserved3; /* Reserved */ 253 | } __attribute__((packed)) XATTR; 254 | 255 | /* This is what Fstat64 wants. */ 256 | struct mint_stat { 257 | int64_t mst_dev; /* Device. */ 258 | uint32_t mst_ino; /* File serial number. */ 259 | uint32_t mst_mode; /* File mode. */ 260 | uint32_t mst_nlink; /* (Hard) link count. */ 261 | uint32_t mst_uid; /* User ID of the file's owner. */ 262 | uint32_t mst_gid; /* Group ID of the file's group. */ 263 | int64_t mst_rdev; /* Device number, if device. */ 264 | int32_t mst_high_atime; 265 | int32_t mst_atime; /* Time of last access, UTC. */ 266 | uint32_t mst_atim; 267 | int32_t mst_high_mtime; 268 | int32_t mst_mtime; /* Time of last access, UTC. */ 269 | uint32_t mst_mtim; 270 | int32_t mst_high_ctime; 271 | int32_t mst_ctime; /* Time of last status change, UTC. */ 272 | uint32_t mst_ctim; 273 | int64_t mst_size; /* File size, in bytes. */ 274 | int64_t mst_blocks; /* Number of 512-bytes blocks allocated. */ 275 | uint32_t mst_blksize; /* Optimal blocksize for I/O. */ 276 | uint32_t mst_flags; /* User defined flags for file. */ 277 | uint32_t mst_gen; /* File generation number. */ 278 | int32_t __res[7]; 279 | } __attribute__((packed)); 280 | 281 | struct mint_pollfd 282 | { 283 | int32_t fd; /* File descriptor to poll */ 284 | uint16_t events; /* Types of events poller cares about */ 285 | uint16_t revents; /* Types of events that actually occurred */ 286 | } __attribute__((packed)); 287 | 288 | struct mint_sgttyb 289 | { 290 | int8_t sg_ispeed; 291 | int8_t sg_ospeed; 292 | int8_t sg_erase; 293 | int8_t sg_kill; 294 | uint16_t sg_flags; 295 | } __attribute__((packed)); 296 | 297 | 298 | extern emuptr32_t current_process; // Current process 299 | -------------------------------------------------------------------------------- /gemdos/signals.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "common.h" 7 | #include "tos_errors.h" 8 | #include "gemdos.h" 9 | 10 | enum mint_signal 11 | { 12 | MINT_SIGNULL = 0, /* not really a signal */ 13 | MINT_SIGHUP = 1, /* hangup signal */ 14 | MINT_SIGINT = 2, /* sent by ^C */ 15 | MINT_SIGQUIT = 3, /* quit signal */ 16 | MINT_SIGILL = 4, /* illegal instruction */ 17 | MINT_SIGTRAP = 5, /* trace trap */ 18 | MINT_SIGABRT = 6, /* abort signal */ 19 | MINT_SIGPRIV = 7, /* privilege violation */ 20 | MINT_SIGFPE = 8, /* divide by zero */ 21 | MINT_SIGKILL = 9, /* cannot be ignored */ 22 | MINT_SIGBUS = 10, /* bus error */ 23 | MINT_SIGSEGV = 11, /* illegal memory reference */ 24 | MINT_SIGSYS = 12, /* bad argument to a system call */ 25 | MINT_SIGPIPE = 13, /* broken pipe */ 26 | MINT_SIGALRM = 14, /* alarm clock */ 27 | MINT_SIGTERM = 15, /* software termination signal */ 28 | MINT_SIGURG = 16, /* urgent condition on I/O channel */ 29 | MINT_SIGSTOP = 17, /* stop signal not from terminal */ 30 | MINT_SIGTSTP = 18, /* stop signal from terminal */ 31 | MINT_SIGCONT = 19, /* continue stopped process */ 32 | MINT_SIGCHLD = 20, /* child stopped or exited */ 33 | MINT_SIGTTIN = 21, /* read by background process */ 34 | MINT_SIGTTOU = 22, /* write by background process */ 35 | MINT_SIGIO = 23, /* I/O possible on a descriptor */ 36 | MINT_SIGXCPU = 24, /* CPU time exhausted */ 37 | MINT_SIGXFSZ = 25, /* file size limited exceeded */ 38 | MINT_SIGVTALRM = 26, /* virtual timer alarm */ 39 | MINT_SIGPROF = 27, /* profiling timer expired */ 40 | MINT_SIGWINCH = 28, /* window size changed */ 41 | MINT_SIGUSR1 = 29, /* user signal 1 */ 42 | MINT_SIGUSR2 = 30, /* user signal 2 */ 43 | MINT_SIGPWR = 31 /* power failure (restart) */ 44 | }; 45 | 46 | static inline uint32_t signal_to_mask(enum mint_signal signal) 47 | { 48 | return 1 << signal; 49 | } 50 | 51 | void mask_to_sigset(sigset_t* set, uint32_t mask) 52 | { 53 | sigemptyset(set); 54 | if (mask & signal_to_mask(MINT_SIGHUP)) 55 | sigaddset(set, SIGHUP); 56 | if (mask & signal_to_mask(MINT_SIGINT)) 57 | sigaddset(set, SIGINT); 58 | if (mask & signal_to_mask(MINT_SIGQUIT)) 59 | sigaddset(set, SIGQUIT); 60 | if (mask & signal_to_mask(MINT_SIGILL)) 61 | sigaddset(set, SIGILL); 62 | if (mask & signal_to_mask(MINT_SIGTRAP)) 63 | sigaddset(set, SIGTRAP); 64 | if (mask & signal_to_mask(MINT_SIGABRT)) 65 | sigaddset(set, SIGABRT); 66 | //if (mask & signal_to_mask(MINT_SIGPRIV)) 67 | // sigaddset(set, SIGPRIV); 68 | if (mask & signal_to_mask(MINT_SIGFPE)) 69 | sigaddset(set, SIGFPE); 70 | if (mask & signal_to_mask(MINT_SIGKILL)) 71 | sigaddset(set, SIGKILL); 72 | if (mask & signal_to_mask(MINT_SIGBUS)) 73 | sigaddset(set, SIGBUS); 74 | if (mask & signal_to_mask(MINT_SIGSEGV)) 75 | sigaddset(set, SIGSEGV); 76 | if (mask & signal_to_mask(MINT_SIGSYS)) 77 | sigaddset(set, SIGSYS); 78 | if (mask & signal_to_mask(MINT_SIGPIPE)) 79 | sigaddset(set, SIGPIPE); 80 | if (mask & signal_to_mask(MINT_SIGALRM)) 81 | sigaddset(set, SIGALRM); 82 | if (mask & signal_to_mask(MINT_SIGTERM)) 83 | sigaddset(set, SIGTERM); 84 | if (mask & signal_to_mask(MINT_SIGURG)) 85 | sigaddset(set, SIGURG); 86 | if (mask & signal_to_mask(MINT_SIGSTOP)) 87 | sigaddset(set, SIGSTOP); 88 | if (mask & signal_to_mask(MINT_SIGTSTP)) 89 | sigaddset(set, SIGTSTP); 90 | if (mask & signal_to_mask(MINT_SIGCONT)) 91 | sigaddset(set, SIGCONT); 92 | if (mask & signal_to_mask(MINT_SIGCHLD)) 93 | sigaddset(set, SIGCHLD); 94 | if (mask & signal_to_mask(MINT_SIGTTIN)) 95 | sigaddset(set, SIGTTIN); 96 | if (mask & signal_to_mask(MINT_SIGTTOU)) 97 | sigaddset(set, SIGTTOU); 98 | if (mask & signal_to_mask(MINT_SIGIO)) 99 | sigaddset(set, SIGIO); 100 | if (mask & signal_to_mask(MINT_SIGXCPU)) 101 | sigaddset(set, SIGXCPU); 102 | if (mask & signal_to_mask(MINT_SIGXFSZ)) 103 | sigaddset(set, SIGXFSZ); 104 | if (mask & signal_to_mask(MINT_SIGVTALRM)) 105 | sigaddset(set, SIGVTALRM); 106 | if (mask & signal_to_mask(MINT_SIGPROF)) 107 | sigaddset(set, SIGPROF); 108 | if (mask & signal_to_mask(MINT_SIGWINCH)) 109 | sigaddset(set, SIGWINCH); 110 | if (mask & signal_to_mask(MINT_SIGUSR1)) 111 | sigaddset(set, SIGUSR1); 112 | if (mask & signal_to_mask(MINT_SIGUSR2)) 113 | sigaddset(set, SIGUSR2); 114 | #ifdef SIGPWR 115 | if (mask & signal_to_mask(MINT_SIGPWR)) 116 | sigaddset(set, SIGPWR); 117 | #endif 118 | } 119 | 120 | uint32_t sigset_to_mask(sigset_t* set) 121 | { 122 | uint32_t mask = 0; 123 | if (sigismember(set, SIGHUP)) 124 | mask |= signal_to_mask(MINT_SIGHUP); 125 | if (sigismember(set, SIGINT)) 126 | mask |= signal_to_mask(MINT_SIGINT); 127 | if (sigismember(set, SIGQUIT)) 128 | mask |= signal_to_mask(MINT_SIGQUIT); 129 | if (sigismember(set, SIGILL)) 130 | mask |= signal_to_mask(MINT_SIGILL); 131 | if (sigismember(set, SIGTRAP)) 132 | mask |= signal_to_mask(MINT_SIGTRAP); 133 | if (sigismember(set, SIGABRT)) 134 | mask |= signal_to_mask(MINT_SIGABRT); 135 | //if (sigismember(set, SIGPRIV)) 136 | // mask |= signal_to_mask(MINT_SIGPRIV); 137 | if (sigismember(set, SIGFPE)) 138 | mask |= signal_to_mask(MINT_SIGFPE); 139 | if (sigismember(set, SIGKILL)) 140 | mask |= signal_to_mask(MINT_SIGKILL); 141 | if (sigismember(set, SIGBUS)) 142 | mask |= signal_to_mask(MINT_SIGBUS); 143 | if (sigismember(set, SIGSEGV)) 144 | mask |= signal_to_mask(MINT_SIGSEGV); 145 | if (sigismember(set, SIGSYS)) 146 | mask |= signal_to_mask(MINT_SIGSYS); 147 | if (sigismember(set, SIGPIPE)) 148 | mask |= signal_to_mask(MINT_SIGPIPE); 149 | if (sigismember(set, SIGALRM)) 150 | mask |= signal_to_mask(MINT_SIGALRM); 151 | if (sigismember(set, SIGTERM)) 152 | mask |= signal_to_mask(MINT_SIGTERM); 153 | if (sigismember(set, SIGURG)) 154 | mask |= signal_to_mask(MINT_SIGURG); 155 | if (sigismember(set, SIGSTOP)) 156 | mask |= signal_to_mask(MINT_SIGSTOP); 157 | if (sigismember(set, SIGTSTP)) 158 | mask |= signal_to_mask(MINT_SIGTSTP); 159 | if (sigismember(set, SIGCONT)) 160 | mask |= signal_to_mask(MINT_SIGCONT); 161 | if (sigismember(set, SIGCHLD)) 162 | mask |= signal_to_mask(MINT_SIGCHLD); 163 | if (sigismember(set, SIGTTIN)) 164 | mask |= signal_to_mask(MINT_SIGTTIN); 165 | if (sigismember(set, SIGTTOU)) 166 | mask |= signal_to_mask(MINT_SIGTTOU); 167 | if (sigismember(set, SIGIO)) 168 | mask |= signal_to_mask(MINT_SIGIO); 169 | if (sigismember(set, SIGXCPU)) 170 | mask |= signal_to_mask(MINT_SIGXCPU); 171 | if (sigismember(set, SIGXFSZ)) 172 | mask |= signal_to_mask(MINT_SIGXFSZ); 173 | if (sigismember(set, SIGVTALRM)) 174 | mask |= signal_to_mask(MINT_SIGVTALRM); 175 | if (sigismember(set, SIGPROF)) 176 | mask |= signal_to_mask(MINT_SIGPROF); 177 | if (sigismember(set, SIGWINCH)) 178 | mask |= signal_to_mask(MINT_SIGWINCH); 179 | if (sigismember(set, SIGUSR1)) 180 | mask |= signal_to_mask(MINT_SIGUSR1); 181 | if (sigismember(set, SIGUSR2)) 182 | mask |= signal_to_mask(MINT_SIGUSR2); 183 | #ifdef SIGPWR 184 | if (sigismember(set, SIGPWR)) 185 | mask |= signal_to_mask(MINT_SIGPWR); 186 | #endif 187 | return mask; 188 | } 189 | 190 | /** 191 | * Pause - 289 192 | * 193 | * The function Pause suspends the calling process until a signal for this 194 | * arrives. If a signal-handler has been installed for this signal with 195 | * Psignal, the handler will be called before the function Pause returns. 196 | * 197 | * The function will not return if the signal-handler executes a non-local 198 | * jump (via longjump), or if the program is terminated. 199 | */ 200 | void Pause ( void ) 201 | { 202 | pause(); 203 | } 204 | 205 | /** 206 | * Pkill - 273 207 | * 208 | * The function Pkill sends the signal sig to one or more processes. The 209 | * following apply for the parameter pid: pid Meaning 210 | */ 211 | int16_t Pkill ( int16_t pid, int16_t sig ) 212 | { 213 | NOT_IMPLEMENTED(GEMDOS, Pkill, 273); 214 | return TOS_ENOSYS; 215 | } 216 | 217 | /** 218 | * Psigaction - 311 219 | * 220 | * The function Psigaction alters the response to the signal sig. The 221 | * parameter act for this is either NULL, or points to a sigaction structure 222 | * that describes the behaviour of the signal handling. The following applies 223 | * for the component sa_handler: sa_handler Meaning 224 | * 225 | * int32_t Psigaction ( int16_t sig, struct sigaction *act, struct sigaction *oact ) 226 | */ 227 | int32_t Psigaction ( int16_t sig, emuptr32_t act, emuptr32_t oact ) 228 | { 229 | NOT_IMPLEMENTED(GEMDOS, Psigaction, 311); 230 | return TOS_ENOSYS; 231 | } 232 | 233 | /** 234 | * Psigblock - 278 235 | * 236 | * The function Psigblock blocks selected signals from delivery. It adds the 237 | * signals specified in mask to the set of currently blocking signals. For 238 | * this, each bit of the parameter mask represents one signal. If bit n in 239 | * mask is set, it means that the signal with the number n will be blocked. 240 | * 241 | * One should note that some signals (e.g. SIGKILL) can not be blocked. The 242 | * kernel will delete these signals from mask before any change of the signal 243 | * set is performed. 244 | * 245 | * Furthermore it should be pointed out that blocked signals also remain 246 | * blocked via Pfork/Pvfork calls. After a Pexec call the child always starts 247 | * with an empty set of signals to be blocked, irrespective of which signals 248 | * were blocked by its parent. 249 | * 250 | * Warning: This function is optional, hence a call may be answered with 251 | * EINVFN. 252 | */ 253 | int32_t Psigblock ( int32_t mask ) 254 | { 255 | sigset_t set, old; 256 | mask_to_sigset(&set, mask); 257 | if(sigprocmask(SIG_BLOCK, &set, &old) == -1) 258 | return MapErrno(); 259 | return sigset_to_mask(&old); 260 | } 261 | 262 | /** 263 | * Psigintr - 318 264 | * 265 | * The function Psigintr assigns a signal to a particular exception vector. 266 | * When the exception occurs, the kernel will send the signal to the process. 267 | * 268 | * vec specifies the exception vector. This is the same value as specified 269 | * for Setexc() call. sig specifies the signal number that is supposed to be 270 | * delivered when an exception assigned to the vector vec occurs. When both 271 | * sig and vec are zero, all handlers installed by your program are removed. 272 | * 273 | * You should install a signal-handler prior to making this call, otherwise 274 | * your process will most probably get killed by the first occurrence of the 275 | * interrupt assigned to vec vector.(!nl) Also notice that the function is 276 | * not available on machines equipped with 68000 and 68010 processors. 277 | * 278 | * This function has been totally rewritten as of MiNT version 1.15.1. 279 | * However, the only change visible to programs is that the old value of vec 280 | * is no longer returned (it had little use anyway). Also, since long stack 281 | * frames are needed, a 68020 or newer processor is required. 282 | * 283 | * The handler set up by Psigintr gets removed when your process terminates. 284 | */ 285 | int32_t Psigintr ( int16_t vec, int16_t sig ) 286 | { 287 | NOT_IMPLEMENTED(GEMDOS, Psigintr, 318); 288 | return TOS_ENOSYS; 289 | } 290 | 291 | /** 292 | * Psignal - 274 293 | * 294 | * The function Psignal alters the action to be taken when the signal sig 295 | * arrives. The parameter handler can assume 3 possible values: handler 296 | * Meaning 297 | */ 298 | int32_t Psignal ( int16_t sig, int32_t handler ) 299 | { 300 | NOT_IMPLEMENTED(GEMDOS, Psignal, 274); 301 | return TOS_ENOSYS; 302 | } 303 | 304 | /** 305 | * Psigpause - 310 306 | * 307 | * The function Psigpause sets a new signal mask mask, and suspends the 308 | * called process until a signal arrives that is not masked or ignored. 309 | * 310 | * If a handler has been installed for this signal with Psignal, then this 311 | * will be called before the function returns. If the handler executes a 312 | * longjump to another part of the program, or the process terminates, then 313 | * the function will never return. 314 | * 315 | * Note: When the function returns, the signal mask will be reset to the 316 | * value that applied before the call of Psigpause. Thus the signal mask set 317 | * by the function is only valid temporarily. In MagiC, problems may arise if 318 | * several threads call this function at the same time, as the signal mask 319 | * here is process-global. 320 | */ 321 | int32_t Psigpause ( int32_t mask ) 322 | { 323 | sigset_t set; 324 | mask_to_sigset(&set, mask); 325 | sigsuspend(&set); 326 | return MapErrno(); 327 | } 328 | 329 | /** 330 | * Psigpending - 291 331 | * 332 | * The function Psigpending returns the signals that have been sent to the 333 | * calling process, but not yet handled (say because they are blocked with 334 | * Psigblock or because they are currently being processed). 335 | */ 336 | int32_t Psigpending ( void ) 337 | { 338 | sigset_t set; 339 | if (sigpending(&set) == -1) 340 | return MapErrno(); 341 | return sigset_to_mask(&set); 342 | } 343 | 344 | /** 345 | * Psigreturn - 282 346 | * 347 | * The function Psigreturn prepares for the exit from a signal-handler. As 348 | * this happens automatically when the handler returns, calling Psigreturn is 349 | * only necessary if the handler executes a non-local jump (perhaps with 350 | * longjump) rather than using RTS. 351 | * 352 | * The call has no effect when no signal is being processed at the time. 353 | * 354 | * In MagiC, the thread of the active signal-handler will become the main 355 | * thread of the process and then be removed. All other signal processes will 356 | * be removed as well (nesting). The locked semaphores of the main thread 357 | * will be released, and the supervisor stack will be reset to the value at 358 | * process start. 359 | */ 360 | void Psigreturn ( void ) 361 | { 362 | NOT_IMPLEMENTED(GEMDOS, Psigreturn, 282); 363 | } 364 | 365 | /** 366 | * Psigsetmask - 279 367 | * 368 | * The function Psigsetmask replaces the set of signals that are currently to 369 | * be blocked completely by the signals specified in the parameter mask. 370 | * 371 | * One should note that some signals (e.g. SIGKILL) can not be blocked. The 372 | * kernel will delete these signals from mask before any change of the signal 373 | * set is performed. 374 | * 375 | * Furthermore it should be pointed out that blocked signals also remain 376 | * blocked via Pfork/Pvfork calls. After a Pexec call the child always starts 377 | * with an empty set of signals to be blocked, irrespective of which signals 378 | * were blocked by its parent. 379 | */ 380 | int32_t Psigsetmask ( int32_t mask ) 381 | { 382 | sigset_t set, old; 383 | mask_to_sigset(&set, mask); 384 | if(sigprocmask(SIG_SETMASK, &set, &old) == -1) 385 | return MapErrno(); 386 | return sigset_to_mask(&old); 387 | } 388 | -------------------------------------------------------------------------------- /gemdos/gemdos_dispatch.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "m68k.h" 3 | #include "m68kcpu.h" 4 | #include "gemdos.h" 5 | 6 | void dispatch_gemdos_trap() 7 | { 8 | uint32_t sp = m68k_get_reg(NULL, M68K_REG_SP); 9 | uint16_t num = m68k_read_memory_16(sp); 10 | int32_t retval = 0; 11 | switch (num) { 12 | case 0 : 13 | Pterm0(); 14 | break; 15 | case 1 : 16 | retval = Cconin(); 17 | break; 18 | case 2 : 19 | retval = Cconout(m68k_read_memory_16(sp+2)); 20 | break; 21 | case 3 : 22 | retval = Cauxin(); 23 | break; 24 | case 4 : 25 | retval = Cauxout(m68k_read_memory_16(sp+2)); 26 | break; 27 | case 5 : 28 | retval = Cprnout(m68k_read_memory_16(sp+2)); 29 | break; 30 | case 6 : 31 | retval = Crawio(m68k_read_memory_16(sp+2)); 32 | break; 33 | case 7 : 34 | retval = Crawcin(); 35 | break; 36 | case 8 : 37 | retval = Cnecin(); 38 | break; 39 | case 9 : 40 | retval = Cconws(m68k_read_memory_32(sp+2)); 41 | break; 42 | case 10 : 43 | retval = Cconrs(m68k_read_memory_32(sp+2)); 44 | break; 45 | case 11 : 46 | retval = Cconis(); 47 | break; 48 | case 14 : 49 | retval = Dsetdrv(m68k_read_memory_16(sp+2)); 50 | break; 51 | case 16 : 52 | retval = Cconos(); 53 | break; 54 | case 17 : 55 | retval = Cprnos(); 56 | break; 57 | case 18 : 58 | retval = Cauxis(); 59 | break; 60 | case 19 : 61 | retval = Cauxos(); 62 | break; 63 | case 20 : 64 | retval = Maddalt(m68k_read_memory_32(sp+2), m68k_read_memory_32(sp+6)); 65 | break; 66 | case 21 : 67 | retval = Srealloc(m68k_read_memory_32(sp+2)); 68 | break; 69 | case 22 : 70 | retval = Slbopen(m68k_read_memory_32(sp+2), m68k_read_memory_32(sp+6), m68k_read_memory_32(sp+10), m68k_read_memory_32(sp+14), m68k_read_memory_32(sp+18)); 71 | break; 72 | case 23 : 73 | retval = Slbclose(m68k_read_memory_32(sp+2)); 74 | break; 75 | case 25 : 76 | retval = Dgetdrv(); 77 | break; 78 | case 26 : 79 | Fsetdta(m68k_read_memory_32(sp+2)); 80 | break; 81 | case 32 : 82 | retval = Super(m68k_read_memory_32(sp+2)); 83 | break; 84 | case 42 : 85 | retval = Tgetdate(); 86 | break; 87 | case 43 : 88 | retval = Tsetdate(m68k_read_memory_16(sp+2)); 89 | break; 90 | case 44 : 91 | retval = Tgettime(); 92 | break; 93 | case 45 : 94 | retval = Tsettime(m68k_read_memory_16(sp+2)); 95 | break; 96 | case 47 : 97 | retval = Fgetdta(); 98 | break; 99 | case 48 : 100 | retval = Sversion(); 101 | break; 102 | case 49 : 103 | Ptermres(m68k_read_memory_32(sp+2), m68k_read_memory_16(sp+6)); 104 | break; 105 | case 51 : 106 | retval = Sconfig(m68k_read_memory_16(sp+2), m68k_read_memory_32(sp+4)); 107 | break; 108 | case 54 : 109 | retval = Dfree(m68k_read_memory_32(sp+2), m68k_read_memory_16(sp+6)); 110 | break; 111 | case 57 : 112 | retval = Dcreate(m68k_read_memory_32(sp+2)); 113 | break; 114 | case 58 : 115 | retval = Ddelete(m68k_read_memory_32(sp+2)); 116 | break; 117 | case 59 : 118 | retval = Dsetpath(m68k_read_memory_32(sp+2)); 119 | break; 120 | case 60 : 121 | retval = Fcreate(m68k_read_memory_32(sp+2), m68k_read_memory_16(sp+6)); 122 | break; 123 | case 61 : 124 | retval = Fopen(m68k_read_memory_32(sp+2), m68k_read_memory_16(sp+6)); 125 | break; 126 | case 62 : 127 | retval = Fclose(m68k_read_memory_16(sp+2)); 128 | break; 129 | case 63 : 130 | retval = Fread(m68k_read_memory_16(sp+2), m68k_read_memory_32(sp+4), m68k_read_memory_32(sp+8)); 131 | break; 132 | case 64 : 133 | retval = Fwrite(m68k_read_memory_16(sp+2), m68k_read_memory_32(sp+4), m68k_read_memory_32(sp+8)); 134 | break; 135 | case 65 : 136 | retval = Fdelete(m68k_read_memory_32(sp+2)); 137 | break; 138 | case 66 : 139 | retval = Fseek(m68k_read_memory_32(sp+2), m68k_read_memory_16(sp+6), m68k_read_memory_16(sp+8)); 140 | break; 141 | case 67 : 142 | retval = Fattrib(m68k_read_memory_32(sp+2), m68k_read_memory_16(sp+6), m68k_read_memory_16(sp+8)); 143 | break; 144 | case 68 : 145 | retval = Mxalloc(m68k_read_memory_32(sp+2), m68k_read_memory_16(sp+6)); 146 | break; 147 | case 69 : 148 | retval = Fdup(m68k_read_memory_16(sp+2)); 149 | break; 150 | case 70 : 151 | retval = Fforce(m68k_read_memory_16(sp+2), m68k_read_memory_16(sp+4)); 152 | break; 153 | case 71 : 154 | retval = Dgetpath(m68k_read_memory_32(sp+2), m68k_read_memory_16(sp+6)); 155 | break; 156 | case 72 : 157 | retval = Malloc(m68k_read_memory_32(sp+2)); 158 | break; 159 | case 73 : 160 | retval = Mfree(m68k_read_memory_32(sp+2)); 161 | break; 162 | case 74 : 163 | retval = Mshrink(m68k_read_memory_32(sp+4), m68k_read_memory_32(sp+8)); 164 | break; 165 | case 75 : 166 | retval = Pexec(m68k_read_memory_16(sp+2), m68k_read_memory_32(sp+4), m68k_read_memory_32(sp+8), m68k_read_memory_32(sp+12)); 167 | break; 168 | case 76 : 169 | Pterm(m68k_read_memory_16(sp+2)); 170 | break; 171 | case 78 : 172 | retval = Fsfirst(m68k_read_memory_32(sp+2), m68k_read_memory_16(sp+6)); 173 | break; 174 | case 79 : 175 | retval = Fsnext(); 176 | break; 177 | case 86 : 178 | retval = Frename(m68k_read_memory_32(sp+4), m68k_read_memory_32(sp+8)); 179 | break; 180 | case 87 : 181 | Fdatime(m68k_read_memory_32(sp+2), m68k_read_memory_16(sp+6), m68k_read_memory_16(sp+8)); 182 | break; 183 | case 92 : 184 | retval = Flock(m68k_read_memory_16(sp+2), m68k_read_memory_16(sp+4), m68k_read_memory_32(sp+6), m68k_read_memory_32(sp+10)); 185 | break; 186 | case 96 : 187 | retval = Nversion(); 188 | break; 189 | case 255 : 190 | Syield(); 191 | break; 192 | case 256 : 193 | retval = Fpipe(m68k_read_memory_32(sp+2)); 194 | break; 195 | case 257 : 196 | retval = Ffchown(m68k_read_memory_16(sp+2), m68k_read_memory_16(sp+4), m68k_read_memory_16(sp+6)); 197 | break; 198 | case 258 : 199 | retval = Ffchmod(m68k_read_memory_16(sp+2), m68k_read_memory_16(sp+4)); 200 | break; 201 | case 260 : 202 | retval = Fcntl(m68k_read_memory_16(sp+2), m68k_read_memory_32(sp+4), m68k_read_memory_16(sp+8)); 203 | break; 204 | case 261 : 205 | retval = Finstat(m68k_read_memory_16(sp+2)); 206 | break; 207 | case 262 : 208 | retval = Foutstat(m68k_read_memory_16(sp+2)); 209 | break; 210 | case 263 : 211 | retval = Fgetchar(m68k_read_memory_16(sp+2), m68k_read_memory_16(sp+4)); 212 | break; 213 | case 264 : 214 | retval = Fputchar(m68k_read_memory_16(sp+2), m68k_read_memory_32(sp+4), m68k_read_memory_16(sp+8)); 215 | break; 216 | case 265 : 217 | retval = Pwait(); 218 | break; 219 | case 266 : 220 | retval = Pnice(m68k_read_memory_16(sp+2)); 221 | break; 222 | case 267 : 223 | retval = Pgetpid(); 224 | break; 225 | case 268 : 226 | retval = Pgetppid(); 227 | break; 228 | case 269 : 229 | retval = Pgetpgrp(); 230 | break; 231 | case 270 : 232 | retval = Psetpgrp(m68k_read_memory_16(sp+2), m68k_read_memory_16(sp+4)); 233 | break; 234 | case 271 : 235 | retval = Pgetuid(); 236 | break; 237 | case 272 : 238 | retval = Psetuid(m68k_read_memory_16(sp+2)); 239 | break; 240 | case 273 : 241 | retval = Pkill(m68k_read_memory_16(sp+2), m68k_read_memory_16(sp+4)); 242 | break; 243 | case 274 : 244 | retval = Psignal(m68k_read_memory_16(sp+2), m68k_read_memory_32(sp+4)); 245 | break; 246 | case 275 : 247 | retval = Pvfork(); 248 | break; 249 | case 276 : 250 | retval = Pgetgid(); 251 | break; 252 | case 277 : 253 | retval = Psetgid(m68k_read_memory_16(sp+2)); 254 | break; 255 | case 278 : 256 | retval = Psigblock(m68k_read_memory_32(sp+2)); 257 | break; 258 | case 279 : 259 | retval = Psigsetmask(m68k_read_memory_32(sp+2)); 260 | break; 261 | case 280 : 262 | retval = Pusrval(m68k_read_memory_32(sp+2)); 263 | break; 264 | case 281 : 265 | retval = Pdomain(m68k_read_memory_16(sp+2)); 266 | break; 267 | case 282 : 268 | Psigreturn(); 269 | break; 270 | case 283 : 271 | retval = Pfork(); 272 | break; 273 | case 284 : 274 | retval = Pwait3(m68k_read_memory_16(sp+2), m68k_read_memory_32(sp+4)); 275 | break; 276 | case 285 : 277 | retval = Fselect(m68k_read_memory_16(sp+2), m68k_read_memory_32(sp+4), m68k_read_memory_32(sp+8), m68k_read_memory_32(sp+12)); 278 | break; 279 | case 286 : 280 | Prusage(m68k_read_memory_32(sp+2)); 281 | break; 282 | case 287 : 283 | retval = Psetlimit(m68k_read_memory_16(sp+2), m68k_read_memory_32(sp+4)); 284 | break; 285 | case 288 : 286 | retval = Talarm(m68k_read_memory_32(sp+2)); 287 | break; 288 | case 289 : 289 | Pause(); 290 | break; 291 | case 290 : 292 | retval = Sysconf(m68k_read_memory_16(sp+2)); 293 | break; 294 | case 291 : 295 | retval = Psigpending(); 296 | break; 297 | case 292 : 298 | retval = Dpathconf(m68k_read_memory_32(sp+2), m68k_read_memory_16(sp+6)); 299 | break; 300 | case 293 : 301 | retval = Pmsg(m68k_read_memory_16(sp+2), m68k_read_memory_32(sp+4), m68k_read_memory_32(sp+8)); 302 | break; 303 | case 294 : 304 | retval = Fmidipipe(m68k_read_memory_16(sp+2), m68k_read_memory_16(sp+4), m68k_read_memory_16(sp+6)); 305 | break; 306 | case 295 : 307 | retval = Prenice(m68k_read_memory_16(sp+2), m68k_read_memory_16(sp+4)); 308 | break; 309 | case 296 : 310 | retval = Dopendir(m68k_read_memory_32(sp+2), m68k_read_memory_16(sp+6)); 311 | break; 312 | case 297 : 313 | retval = Dreaddir(m68k_read_memory_16(sp+2), m68k_read_memory_32(sp+4), m68k_read_memory_32(sp+8)); 314 | break; 315 | case 298 : 316 | retval = Drewinddir(m68k_read_memory_32(sp+2)); 317 | break; 318 | case 299 : 319 | retval = Dclosedir(m68k_read_memory_32(sp+2)); 320 | break; 321 | case 300 : 322 | retval = Fxattr(m68k_read_memory_16(sp+2), m68k_read_memory_32(sp+4), m68k_read_memory_32(sp+8)); 323 | break; 324 | case 301 : 325 | retval = Flink(m68k_read_memory_32(sp+2), m68k_read_memory_32(sp+6)); 326 | break; 327 | case 302 : 328 | retval = Fsymlink(m68k_read_memory_32(sp+2), m68k_read_memory_32(sp+6)); 329 | break; 330 | case 303 : 331 | retval = Freadlink(m68k_read_memory_16(sp+2), m68k_read_memory_32(sp+4), m68k_read_memory_32(sp+8)); 332 | break; 333 | case 304 : 334 | retval = Dcntl(m68k_read_memory_16(sp+2), m68k_read_memory_32(sp+4), m68k_read_memory_32(sp+8)); 335 | break; 336 | case 305 : 337 | retval = Fchown(m68k_read_memory_32(sp+2), m68k_read_memory_16(sp+6), m68k_read_memory_16(sp+8)); 338 | break; 339 | case 306 : 340 | retval = Fchmod(m68k_read_memory_32(sp+2), m68k_read_memory_16(sp+6)); 341 | break; 342 | case 307 : 343 | retval = Pumask(m68k_read_memory_16(sp+2)); 344 | break; 345 | case 308 : 346 | retval = Psemaphore(m68k_read_memory_16(sp+2), m68k_read_memory_32(sp+4), m68k_read_memory_32(sp+8)); 347 | break; 348 | case 309 : 349 | retval = Dlock(m68k_read_memory_16(sp+2), m68k_read_memory_16(sp+4)); 350 | break; 351 | case 310 : 352 | // note we return a value here (usually TOS_EINTR) even though tos.hyp specifies void 353 | retval = Psigpause(m68k_read_memory_32(sp+2)); 354 | break; 355 | case 311 : 356 | retval = Psigaction(m68k_read_memory_16(sp+2), m68k_read_memory_32(sp+4), m68k_read_memory_32(sp+8)); 357 | break; 358 | case 312 : 359 | retval = Pgeteuid(); 360 | break; 361 | case 313 : 362 | retval = Pgetegid(); 363 | break; 364 | case 314 : 365 | retval = Pwaitpid(m68k_read_memory_16(sp+2), m68k_read_memory_16(sp+4), m68k_read_memory_32(sp+6)); 366 | break; 367 | case 315 : 368 | retval = Dgetcwd(m68k_read_memory_32(sp+2), m68k_read_memory_16(sp+6), m68k_read_memory_16(sp+8)); 369 | break; 370 | case 316 : 371 | Salert(m68k_read_memory_32(sp+2)); 372 | break; 373 | case 317 : 374 | retval = Tmalarm(m68k_read_memory_32(sp+2)); 375 | break; 376 | case 318 : 377 | retval = Psigintr(m68k_read_memory_16(sp+2), m68k_read_memory_16(sp+4)); 378 | break; 379 | case 319 : 380 | retval = Suptime(m68k_read_memory_32(sp+2), m68k_read_memory_32(sp+6)); 381 | break; 382 | case 322 : 383 | retval = Dxreaddir(m68k_read_memory_16(sp+2), m68k_read_memory_32(sp+4), m68k_read_memory_32(sp+8), m68k_read_memory_32(sp+12), m68k_read_memory_32(sp+16)); 384 | break; 385 | case 323 : 386 | retval = Pseteuid(m68k_read_memory_16(sp+2)); 387 | break; 388 | case 324 : 389 | retval = Psetegid(m68k_read_memory_16(sp+2)); 390 | break; 391 | case 325 : 392 | retval = Psetauid(m68k_read_memory_16(sp+2)); 393 | break; 394 | case 326 : 395 | retval = Pgetauid(); 396 | break; 397 | case 327 : 398 | retval = Pgetgroups(m68k_read_memory_16(sp+2), m68k_read_memory_32(sp+4)); 399 | break; 400 | case 328 : 401 | retval = Psetgroups(m68k_read_memory_16(sp+2), m68k_read_memory_32(sp+4)); 402 | break; 403 | case 329 : 404 | retval = Tsetitimer(m68k_read_memory_16(sp+2), m68k_read_memory_32(sp+4), m68k_read_memory_32(sp+8), m68k_read_memory_32(sp+12), m68k_read_memory_32(sp+16)); 405 | break; 406 | case 330 : 407 | retval = Dchroot(m68k_read_memory_32(sp+2)); 408 | break; 409 | case 331: 410 | retval = Fstat64(m68k_read_memory_16(sp+2), m68k_read_memory_32(sp+4), m68k_read_memory_32(sp+8)); 411 | break; 412 | case 334 : 413 | retval = Psetreuid(m68k_read_memory_16(sp+2), m68k_read_memory_16(sp+4)); 414 | break; 415 | case 335 : 416 | retval = Psetregid(m68k_read_memory_16(sp+2), m68k_read_memory_16(sp+4)); 417 | break; 418 | case 336 : 419 | Ssync(); 420 | break; 421 | case 337 : 422 | Shutdown(m68k_read_memory_32(sp+2)); 423 | break; 424 | case 338 : 425 | retval = Dreadlabel(m68k_read_memory_32(sp+2), m68k_read_memory_32(sp+6), m68k_read_memory_16(sp+10)); 426 | break; 427 | case 339 : 428 | retval = Dwritelabel(m68k_read_memory_32(sp+2), m68k_read_memory_32(sp+6)); 429 | break; 430 | case 340 : 431 | retval = Ssystem(m68k_read_memory_16(sp+2), m68k_read_memory_32(sp+4), m68k_read_memory_32(sp+8)); 432 | break; 433 | case 341 : 434 | retval = Tgettimeofday(m68k_read_memory_32(sp+2), m68k_read_memory_32(sp+6)); 435 | break; 436 | case 342 : 437 | retval = Tsettimeofday(m68k_read_memory_32(sp+2), m68k_read_memory_32(sp+6)); 438 | break; 439 | case 344 : 440 | retval = Pgetpriority(m68k_read_memory_16(sp+2), m68k_read_memory_16(sp+4)); 441 | break; 442 | case 345 : 443 | retval = Psetpriority(m68k_read_memory_16(sp+2), m68k_read_memory_16(sp+4), m68k_read_memory_16(sp+6)); 444 | break; 445 | case 346 : 446 | retval = Fpoll(m68k_read_memory_32(sp+2), m68k_read_memory_32(sp+6), m68k_read_memory_32(sp+10)); 447 | break; 448 | case 349: 449 | retval = Ffstat64(m68k_read_memory_16(sp+2), m68k_read_memory_32(sp+4)); 450 | break; 451 | case 1296 : 452 | retval = Dxopendir(m68k_read_memory_32(sp+2), m68k_read_memory_16(sp+6)); 453 | break; 454 | default: 455 | retval = TOS_ENOSYS; 456 | } 457 | //fprintf(stderr,"GEMDOS %x -> %x\n", num, retval); 458 | m68k_set_reg(M68K_REG_D0, retval); 459 | } 460 | -------------------------------------------------------------------------------- /gemdos/dir.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "common.h" 11 | #include "gemdos.h" 12 | #include "path.h" 13 | #include "tos_errors.h" 14 | 15 | /** 16 | * Dchroot - 330 17 | * 18 | * The path parameter is a pathname of the folder that will be the new root 19 | * directory for the calling process. Directories and files which are outside 20 | * of the specified tree will no longer be accessible. 21 | * 22 | * Dchroot does not automatically change the current directory to the newly 23 | * specified root. Also, the call needs root privileges to operate properly. 24 | * This function is used by e.g. ftpd to limit the user privileges in 25 | * accessing file-systems. 26 | * 27 | * int32_t Dchroot( int8_t *path ) 28 | */ 29 | int32_t Dchroot( emuptr32_t name ) 30 | { 31 | int16_t retval = TOS_E_OK; 32 | char* path = read_path(name); 33 | if (!path) 34 | return TOS_EPTHNF; 35 | if (chroot(path) == -1) 36 | retval = MapErrno(); 37 | free(path); 38 | return retval; 39 | } 40 | 41 | /** 42 | * Dclosedir - 299 43 | * 44 | * The function Dclosedir closes the directory with the handle dirhandle. 45 | */ 46 | int32_t Dclosedir ( int32_t dirhandle ) 47 | { 48 | int32_t retval = TOS_E_OK; 49 | DIR* dirP = (DIR*)m68k_read_memory_64(dirhandle); 50 | retval = Mfree(dirhandle); 51 | if (retval < 0) 52 | return retval; 53 | if (!closedir(dirP)) 54 | return TOS_EIHNDL; 55 | return retval; 56 | } 57 | 58 | /** 59 | * Dcntl - 304 60 | * 61 | * The function Dcntl executes the command cmd on the file or the directory 62 | * name. Details depend on the file-system to which name refers. The meaning 63 | * of the parameter arg is in turn dependent on cmd. 64 | * 65 | * Normally the Dcntl call is supported by the file-systems U:\\ and U:\DEV. 66 | * 67 | * The unified file-system U:\ knows the following commands: Command 68 | * Description 69 | * 70 | * int32_t Dcntl ( int16_t cmd, int8_t *name, int32_t arg ) 71 | */ 72 | int32_t Dcntl ( int16_t cmd, emuptr32_t name, int32_t arg ) 73 | { 74 | NOT_IMPLEMENTED(GEMDOS, Dcntl, 304); 75 | return TOS_ENOSYS; 76 | } 77 | 78 | /** 79 | * Dcreate - 57 80 | * 81 | * The GEMDOS routine Dcreate creates a directory with the pathname path, 82 | * which has to be terminated with the ASCII character 0, on the specified 83 | * drive. 84 | * 85 | * Note: Due to defective error-handling in GEMDOS Versions MagiC 3, the call 86 | * is passed on by the kernel as xfs_dcreate with creation mode Fxattr 87 | * %0100000111101101 (i.e. a 'directory file' with access permissions 88 | * RWXRwXRwX). The XFS should not delete any files or subdirectories of the 89 | * same name, but return the error-code EACCDN in this case. Invalid 90 | * filenames "." or ".." must also be intercepted by the XFS. 91 | * 92 | * int32_t Dcreate ( CONST int8_t *path ) 93 | */ 94 | int32_t Dcreate ( emuptr32_t name ) 95 | { 96 | int16_t retval = TOS_E_OK; 97 | char* path = read_path(name); 98 | if (!path) 99 | return TOS_EPTHNF; 100 | if (mkdir(path, 0777) == -1) 101 | retval = MapErrno(); 102 | free(path); 103 | return retval; 104 | } 105 | 106 | /** 107 | * Ddelete - 58 108 | * 109 | * The GEMDOS routine Ddelete deletes a directory with the pathname path, 110 | * which may not contain any files or subdirectories. The string path must be 111 | * terminated with the ASCII character 0. 112 | * 113 | * Note: In GEMDOS Versions Dcreate with an immediately following Ddelete did 114 | * not work - only a further Ddelete achieved the desired result. 115 | * 116 | * In MagiC the kernel first checks whether the directory is a current path 117 | * and if appropriate returns an error-message. If possible, only empty 118 | * directories should be deleted; this is however up to the XFS. As of MagiC 119 | * Version 4.01, symbolic links too can be deleted with this function; older 120 | * versions always deleted the directory to which the link pointed. 121 | * 122 | * int32_t Ddelete ( CONST int8_t *path ) 123 | */ 124 | int32_t Ddelete ( emuptr32_t name ) 125 | { 126 | int16_t retval = TOS_E_OK; 127 | char* path = read_path(name); 128 | if (!path) 129 | return TOS_EPTHNF; 130 | if (rmdir(path) == -1) 131 | retval = MapErrno(); 132 | free(path); 133 | return retval; 134 | } 135 | 136 | /** 137 | * Dfree - 54 138 | * 139 | * The GEMDOS routine Dfree obtains the capacity and current occupancy of a 140 | * drive. The following apply: Parameter Meaning 141 | * 142 | * int16_t Dfree ( DISKINFO *buf, int16_t driveno ) 143 | */ 144 | int16_t Dfree ( emuptr32_t buf, int16_t driveno ) 145 | { 146 | NOT_IMPLEMENTED(GEMDOS, Dfree, 54); 147 | return TOS_ENOSYS; 148 | } 149 | 150 | 151 | #define get_current_dir_name() (getcwd(NULL,0)) 152 | 153 | /** 154 | * Dgetcwd - 315 155 | * 156 | * The current working directory of the active process in drive drv is 157 | * returned. The parameter size gives the size of the buffer for 158 | * accommodating the name. 159 | * 160 | * int32_t Dgetcwd ( int8_t *path, int16_t drv, int16_t size ) 161 | */ 162 | int32_t Dgetcwd ( emuptr32_t path, int16_t driveno, int16_t size ) 163 | { 164 | int32_t retval = TOS_EDRIVE; 165 | if(driveno == 0 || driveno == 'u'-'a'+1) 166 | { 167 | char* unix_path=get_current_dir_name(); 168 | size_t required = strlen(unix_path)+1; 169 | if(size >= required) 170 | { 171 | m68k_write_string(path, unix_path, size); 172 | retval = TOS_E_OK; 173 | } 174 | else 175 | { 176 | retval = TOS_ENAMETOOLONG; 177 | } 178 | free(unix_path); 179 | } 180 | return retval; 181 | } 182 | 183 | /** 184 | * Dgetdrv - 25 185 | * 186 | * The GEMDOS routine Dgetdrv obtains the current drive. 187 | */ 188 | int16_t Dgetdrv ( void ) 189 | { 190 | return 'u' - 'a'; 191 | } 192 | 193 | /** 194 | * Dgetpath - 71 195 | * 196 | * The GEMDOS routine Dgetpath obtains the current directory on the drive 197 | * driveno. The designation of the drives are coded in the parameter driveno 198 | * as follows: driveno Meaning 199 | * 200 | * int16_t Dgetpath ( int8_t *path, int16_t driveno ) 201 | */ 202 | int16_t Dgetpath ( emuptr32_t path, int16_t driveno ) 203 | { 204 | if(driveno == 0 || driveno == 'u'-'a'+1) 205 | { 206 | char* unix_path=get_current_dir_name(); 207 | char* tmp=alloca(strlen(unix_path)+1); 208 | 209 | char* d=tmp; 210 | for(const char* s=unix_path;*s;) 211 | { 212 | if(*s == '/') 213 | { 214 | *d++='\\'; 215 | s++; 216 | } 217 | else 218 | { 219 | s=filename8_3(d,s); 220 | while(*d) 221 | d++; 222 | } 223 | } 224 | m68k_write_string(path, tmp, 1024); 225 | free(unix_path); 226 | return TOS_E_OK; 227 | } 228 | else 229 | { 230 | return TOS_EDRIVE; 231 | } 232 | } 233 | 234 | /** 235 | * Dlock - 309 236 | * 237 | * The function Dlock permits the locking of the BIOS device drv, or 238 | * releasing it again. On a locked drive no GEMDOS file operations are 239 | * permitted; Rwabs is only allowed for the locking process. 240 | * 241 | * The purpose of the function is to allow low-level programs (such as those 242 | * for formatting) to perform (X)BIOS functions, while the device remains 243 | * locked for normal file accesses. 244 | * 245 | * Bit 0 of mode = 1: Lock device 246 | */ 247 | int32_t Dlock ( int16_t mode, int16_t drv ) 248 | { 249 | NOT_IMPLEMENTED(GEMDOS, Dlock, 309); 250 | return TOS_ENOSYS; 251 | } 252 | 253 | /** 254 | * Dopendir - 296 255 | * 256 | * The function Dopendir opens the directory name for reading. name should be 257 | * NULL-terminated and should not contain a trailing backslash. The parameter 258 | * flag describes the manner in which the directory is to be opened: flag 259 | * Meaning 260 | * 261 | * int32_t Dopendir ( int8_t *name, int16_t flag ) 262 | */ 263 | int32_t Dopendir ( emuptr32_t name, int16_t flag ) 264 | { 265 | if(flag == 1) 266 | return TOS_ENOSYS; 267 | int32_t retval; 268 | char* path = read_path(name); 269 | if(!path) 270 | return TOS_EPTHNF; 271 | DIR* dirP = opendir(path); 272 | if(dirP) 273 | { 274 | retval = Malloc(16); // Allocate memory to store the dir pointer 275 | m68k_write_memory_64(retval, (uint64_t)dirP); 276 | } 277 | else 278 | { 279 | retval = TOS_EPTHNF; 280 | } 281 | free(path); 282 | return retval; 283 | } 284 | 285 | /** 286 | * Dpathconf - 292 287 | * 288 | * The function Dpathconf makes it possible to obtain information about the 289 | * file-system that contains the file name. 290 | * 291 | * The parameter mode specifies which possibilities or restrictions of the 292 | * file-systems are to be inquired. Possible values here are: mode Meaning 293 | * 294 | * int32_t Dpathconf ( BYTE *name, int16_t mode ) 295 | */ 296 | int32_t Dpathconf ( emuptr32_t name, int16_t mode ) 297 | { 298 | switch (mode) 299 | { 300 | case 0: 301 | return 1024; 302 | case 2: 303 | return PATH_MAX; 304 | case 3: 305 | return NAME_MAX; 306 | case 6: 307 | return 0; 308 | } 309 | NOT_IMPLEMENTED(GEMDOS, Dpathconf_mode, mode); 310 | return TOS_ENOSYS; 311 | } 312 | 313 | /** 314 | * Dreaddir - 297 315 | * 316 | * The function Dreaddir returns the next file in the directory dirhandle. 317 | * The filename and an optional 4-byte index for the file are written to buf. 318 | * 319 | * The file index is omitted if the directory was opened im compatibility 320 | * mode, otherwise it appears as the first in buf followed by a 321 | * (NULL-terminated) filename. 322 | * 323 | * (Possibly different) names with the same index belong to the same file. 324 | * (Possibly same) names with different indexes belong to different files. 325 | * 326 | * Note about MagiC: For FAT file-systems the function returns the following 327 | * index data: Directories: Start cluster in Motorola format Other files: 328 | * High word = Start cluster of the directory 329 | * 330 | * int32_t Dreaddir ( int16_t len, int32_t dirhandle, int8_t *buf ) 331 | */ 332 | int32_t Dreaddir ( int16_t len, int32_t dirhandle, emuptr32_t buf ) 333 | { 334 | int32_t retval = TOS_E_OK; 335 | DIR* dirP = (DIR*)m68k_read_memory_64(dirhandle); 336 | struct dirent *entry = readdir(dirP); 337 | if(! entry) 338 | { 339 | return errno?TOS_EIHNDL:TOS_ENMFIL; 340 | } 341 | m68k_write_memory_32(buf, entry->d_ino&0xffffffff); 342 | m68k_write_string(buf+4, entry->d_name, NAME_MAX); 343 | return TOS_E_OK; 344 | 345 | } 346 | 347 | /** 348 | * Dreadlabel - 338 349 | * 350 | * The function Dreadlabel returns in label the name of the file-system lying 351 | * in path. The parameter length specifies the size of the receiving buffer 352 | * for label. 353 | * 354 | * int32_t Dreadlabel ( CONST int8_t *path, int8_t *label, int16_t length ) 355 | */ 356 | int32_t Dreadlabel ( emuptr32_t path, emuptr32_t label, int16_t length ) 357 | { 358 | NOT_IMPLEMENTED(GEMDOS, Dreadlabel, 338); 359 | return TOS_ENOSYS; 360 | } 361 | 362 | /** 363 | * Drewinddir - 298 364 | * 365 | * The function Drewinddir resets the directory handle to the start, so that 366 | * the next call of Dreaddir reads the first entry in the directory once 367 | * more. 368 | */ 369 | int32_t Drewinddir ( int32_t dirhandle ) 370 | { 371 | int32_t retval = TOS_E_OK; 372 | DIR* dirP = (DIR*)m68k_read_memory_64(dirhandle); 373 | rewinddir(dirP); 374 | return retval; 375 | } 376 | 377 | /** 378 | * Dsetdrv - 14 379 | * 380 | * The GEMDOS routine Dsetdrv makes the drive drv the current default drive. 381 | * The following apply: drv Meaning 382 | */ 383 | int32_t Dsetdrv ( int16_t drv ) 384 | { 385 | return 1 << ('u'-'a'); 386 | } 387 | 388 | /** 389 | * Dsetpath - 59 390 | * 391 | * The GEMDOS routine Dsetpath sets a new access path for the current drive. 392 | * The parameter path contains the new current access path for the drive. 393 | * 394 | * Note: For each drive, GEMDOS will remember the current directory. However, 395 | * the function Dsetpath should be used only for the current drive. For other 396 | * drives the following method should be applied instead: Establish current 397 | * drive Set desired drive Set path for this drive Make the old (remembered) 398 | * drive the current one again 399 | * 400 | * On some (older) versions of GEMDOS, a too frequent setting of non-existing 401 | * paths can lead to disturbance in the internal structures. 402 | * 403 | * int16_t Dsetpath ( CONST int8_t *path ) 404 | */ 405 | int16_t Dsetpath ( emuptr32_t name ) 406 | { 407 | int16_t retval = TOS_E_OK; 408 | char* path = read_path(name); 409 | if (!path) 410 | return TOS_EPTHNF; 411 | if (chdir(path) == -1) 412 | retval = MapErrno(); 413 | free(path); 414 | return retval; 415 | } 416 | 417 | /** 418 | * Dwritelabel - 339 419 | * 420 | * The function Dwritelabel writes the name specified in label to the 421 | * file-system specified in path. 422 | * 423 | * Prior to MiNT version 1.14.5 this call contained a bug that allowed the 424 | * label to be changed by all users. 425 | * 426 | * int32_t Dwritelabel ( CONST int8_t *path, CONST int8_t *label ) 427 | */ 428 | int32_t Dwritelabel ( emuptr32_t path, emuptr32_t label ) 429 | { 430 | NOT_IMPLEMENTED(GEMDOS, Dwritelabel, 339); 431 | return TOS_ENOSYS; 432 | } 433 | 434 | /** 435 | * Dxopendir - 1296 436 | * 437 | * Dxopendir calls the normal Dopendir, but then saves the returned handle 438 | * and flag in a chained list, so that Dreaddir and Dxreaddir know what they 439 | * have to convert for this directory. Dcloseddir deletes the handle again 440 | * from the list. 441 | * 442 | * Dreaddirand Dxreaddir convert the returned name to lower case. 443 | * 444 | * The function opens the directory name for reading. The parameter flag 445 | * describes the manner how the directory is to be opened: flag Meaning 446 | * 447 | * int32_t Dxopendir ( int8_t *name, int16_t flag ) 448 | */ 449 | int32_t Dxopendir ( emuptr32_t name, int16_t flag ) 450 | { 451 | NOT_IMPLEMENTED(GEMDOS, Dxopendir, 1296); 452 | return TOS_ENOSYS; 453 | } 454 | 455 | /** 456 | * Dxreaddir - 322 457 | * 458 | * The function Dxreaddir returns the next file from the directory with the 459 | * handle dirh. The filename and the optional 4 byte long file index are 460 | * stored in the buffer specified in buf. 461 | * 462 | * The file index is omitted if the compatibility mode was specified for 463 | * Dopendir. If two filenames have the same index, they represent the same 464 | * file. 465 | * 466 | * MagiCreturns for FAT file-systems the following index data: Directories: 467 | * Start cluster in Motorola format Other files: High word = Start cluster of 468 | * the directory 469 | * 470 | * int32_t Dxreaddir ( int16_t ln, int32_t dirh, int8_t *buf, XATTR *xattr, int32_t *xr ) 471 | */ 472 | int32_t Dxreaddir ( int16_t ln, int32_t dirh, emuptr32_t buf, emuptr32_t xattr, emuptr32_t xr ) 473 | { 474 | NOT_IMPLEMENTED(GEMDOS, Dxreaddir, 322); 475 | return TOS_ENOSYS; 476 | } 477 | -------------------------------------------------------------------------------- /mushashi/m68k.h: -------------------------------------------------------------------------------- 1 | /* ======================================================================== */ 2 | /* ========================= LICENSING & COPYRIGHT ======================== */ 3 | /* ======================================================================== */ 4 | /* 5 | * MUSASHI 6 | * Version 3.4 7 | * 8 | * A portable Motorola M680x0 processor emulation engine. 9 | * Copyright 1998-2001 Karl Stenerud. All rights reserved. 10 | * 11 | * Permission is hereby granted, free of charge, to any person obtaining a copy 12 | * of this software and associated documentation files (the "Software"), to deal 13 | * in the Software without restriction, including without limitation the rights 14 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | * copies of the Software, and to permit persons to whom the Software is 16 | * furnished to do so, subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be included in 19 | * all copies or substantial portions of the Software. 20 | 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | * THE SOFTWARE. 28 | */ 29 | 30 | #ifndef M68K__HEADER 31 | #define M68K__HEADER 32 | 33 | 34 | /* ======================================================================== */ 35 | /* ============================= CONFIGURATION ============================ */ 36 | /* ======================================================================== */ 37 | 38 | /* Import the configuration for this build */ 39 | #include "m68kconf.h" 40 | 41 | 42 | /* ======================================================================== */ 43 | /* ============================ GENERAL DEFINES =========================== */ 44 | 45 | /* ======================================================================== */ 46 | 47 | /* There are 7 levels of interrupt to the 68K. 48 | * A transition from < 7 to 7 will cause a non-maskable interrupt (NMI). 49 | */ 50 | #define M68K_IRQ_NONE 0 51 | #define M68K_IRQ_1 1 52 | #define M68K_IRQ_2 2 53 | #define M68K_IRQ_3 3 54 | #define M68K_IRQ_4 4 55 | #define M68K_IRQ_5 5 56 | #define M68K_IRQ_6 6 57 | #define M68K_IRQ_7 7 58 | 59 | 60 | /* Special interrupt acknowledge values. 61 | * Use these as special returns from the interrupt acknowledge callback 62 | * (specified later in this header). 63 | */ 64 | 65 | /* Causes an interrupt autovector (0x18 + interrupt level) to be taken. 66 | * This happens in a real 68K if VPA or AVEC is asserted during an interrupt 67 | * acknowledge cycle instead of DTACK. 68 | */ 69 | #define M68K_INT_ACK_AUTOVECTOR 0xffffffff 70 | 71 | /* Causes the spurious interrupt vector (0x18) to be taken 72 | * This happens in a real 68K if BERR is asserted during the interrupt 73 | * acknowledge cycle (i.e. no devices responded to the acknowledge). 74 | */ 75 | #define M68K_INT_ACK_SPURIOUS 0xfffffffe 76 | 77 | 78 | /* CPU types for use in m68k_set_cpu_type() */ 79 | enum 80 | { 81 | M68K_CPU_TYPE_INVALID, 82 | M68K_CPU_TYPE_68000, 83 | M68K_CPU_TYPE_68010, 84 | M68K_CPU_TYPE_68EC020, 85 | M68K_CPU_TYPE_68020, 86 | M68K_CPU_TYPE_68030, /* Supported by disassembler ONLY */ 87 | M68K_CPU_TYPE_68040 /* Supported by disassembler ONLY */ 88 | }; 89 | 90 | /* Registers used by m68k_get_reg() and m68k_set_reg() */ 91 | typedef enum 92 | { 93 | /* Real registers */ 94 | M68K_REG_D0, /* Data registers */ 95 | M68K_REG_D1, 96 | M68K_REG_D2, 97 | M68K_REG_D3, 98 | M68K_REG_D4, 99 | M68K_REG_D5, 100 | M68K_REG_D6, 101 | M68K_REG_D7, 102 | M68K_REG_A0, /* Address registers */ 103 | M68K_REG_A1, 104 | M68K_REG_A2, 105 | M68K_REG_A3, 106 | M68K_REG_A4, 107 | M68K_REG_A5, 108 | M68K_REG_A6, 109 | M68K_REG_A7, 110 | M68K_REG_PC, /* Program Counter */ 111 | M68K_REG_SR, /* Status Register */ 112 | M68K_REG_SP, /* The current Stack Pointer (located in A7) */ 113 | M68K_REG_USP, /* User Stack Pointer */ 114 | M68K_REG_ISP, /* Interrupt Stack Pointer */ 115 | M68K_REG_MSP, /* Master Stack Pointer */ 116 | M68K_REG_SFC, /* Source Function Code */ 117 | M68K_REG_DFC, /* Destination Function Code */ 118 | M68K_REG_VBR, /* Vector Base Register */ 119 | M68K_REG_CACR, /* Cache Control Register */ 120 | M68K_REG_CAAR, /* Cache Address Register */ 121 | 122 | /* Assumed registers */ 123 | /* These are cheat registers which emulate the 1-longword prefetch 124 | * present in the 68000 and 68010. 125 | */ 126 | M68K_REG_PREF_ADDR, /* Last prefetch address */ 127 | M68K_REG_PREF_DATA, /* Last prefetch data */ 128 | 129 | /* Convenience registers */ 130 | M68K_REG_PPC, /* Previous value in the program counter */ 131 | M68K_REG_IR, /* Instruction register */ 132 | M68K_REG_CPU_TYPE /* Type of CPU being run */ 133 | } m68k_register_t; 134 | 135 | /* ======================================================================== */ 136 | /* ====================== FUNCTIONS CALLED BY THE CPU ===================== */ 137 | /* ======================================================================== */ 138 | 139 | /* You will have to implement these functions */ 140 | 141 | /* read/write functions called by the CPU to access memory. 142 | * while values used are 32 bits, only the appropriate number 143 | * of bits are relevant (i.e. in write_memory_8, only the lower 8 bits 144 | * of value should be written to memory). 145 | * 146 | * NOTE: I have separated the immediate and PC-relative memory fetches 147 | * from the other memory fetches because some systems require 148 | * differentiation between PROGRAM and DATA fetches (usually 149 | * for security setups such as encryption). 150 | * This separation can either be achieved by setting 151 | * M68K_SEPARATE_READS in m68kconf.h and defining 152 | * the read functions, or by setting M68K_EMULATE_FC and 153 | * making a function code callback function. 154 | * Using the callback offers better emulation coverage 155 | * because you can also monitor whether the CPU is in SYSTEM or 156 | * USER mode, but it is also slower. 157 | */ 158 | 159 | #ifndef INLINE_MEMORY_ACC 160 | /* Read from anywhere */ 161 | unsigned int m68k_read_memory_8(unsigned int address); 162 | unsigned int m68k_read_memory_16(unsigned int address); 163 | unsigned int m68k_read_memory_32(unsigned int address); 164 | #endif 165 | 166 | /* Read data immediately following the PC */ 167 | unsigned int m68k_read_immediate_16(unsigned int address); 168 | unsigned int m68k_read_immediate_32(unsigned int address); 169 | 170 | /* Read data relative to the PC */ 171 | unsigned int m68k_read_pcrelative_8(unsigned int address); 172 | unsigned int m68k_read_pcrelative_16(unsigned int address); 173 | unsigned int m68k_read_pcrelative_32(unsigned int address); 174 | 175 | /* Memory access for the disassembler */ 176 | unsigned int m68k_read_disassembler_8 (unsigned int address); 177 | unsigned int m68k_read_disassembler_16 (unsigned int address); 178 | unsigned int m68k_read_disassembler_32 (unsigned int address); 179 | 180 | /* Write to anywhere */ 181 | #ifndef INLINE_MEMORY_ACC 182 | void m68k_write_memory_8(unsigned int address, unsigned int value); 183 | void m68k_write_memory_16(unsigned int address, unsigned int value); 184 | void m68k_write_memory_32(unsigned int address, unsigned int value); 185 | #endif 186 | /* Special call to simulate undocumented 68k behavior when move.l with a 187 | * predecrement destination mode is executed. 188 | * To simulate real 68k behavior, first write the high word to 189 | * [address+2], and then write the low word to [address]. 190 | * 191 | * Enable this functionality with M68K_SIMULATE_PD_WRITES in m68kconf.h. 192 | */ 193 | void m68k_write_memory_32_pd(unsigned int address, unsigned int value); 194 | 195 | 196 | 197 | /* ======================================================================== */ 198 | /* ============================== CALLBACKS =============================== */ 199 | /* ======================================================================== */ 200 | 201 | /* These functions allow you to set callbacks to the host when specific events 202 | * occur. Note that you must enable the corresponding value in m68kconf.h 203 | * in order for these to do anything useful. 204 | * Note: I have defined default callbacks which are used if you have enabled 205 | * the corresponding #define in m68kconf.h but either haven't assigned a 206 | * callback or have assigned a callback of NULL. 207 | */ 208 | 209 | /* Set the callback for an interrupt acknowledge. 210 | * You must enable M68K_EMULATE_INT_ACK in m68kconf.h. 211 | * The CPU will call the callback with the interrupt level being acknowledged. 212 | * The host program must return either a vector from 0x02-0xff, or one of the 213 | * special interrupt acknowledge values specified earlier in this header. 214 | * If this is not implemented, the CPU will always assume an autovectored 215 | * interrupt, and will automatically clear the interrupt request when it 216 | * services the interrupt. 217 | * Default behavior: return M68K_INT_ACK_AUTOVECTOR. 218 | */ 219 | void m68k_set_int_ack_callback(int (*callback)(int int_level)); 220 | 221 | 222 | /* Set the callback for a breakpoint acknowledge (68010+). 223 | * You must enable M68K_EMULATE_BKPT_ACK in m68kconf.h. 224 | * The CPU will call the callback with whatever was in the data field of the 225 | * BKPT instruction for 68020+, or 0 for 68010. 226 | * Default behavior: do nothing. 227 | */ 228 | void m68k_set_bkpt_ack_callback(void (*callback)(unsigned int data)); 229 | 230 | 231 | /* Set the callback for the RESET instruction. 232 | * You must enable M68K_EMULATE_RESET in m68kconf.h. 233 | * The CPU calls this callback every time it encounters a RESET instruction. 234 | * Default behavior: do nothing. 235 | */ 236 | void m68k_set_reset_instr_callback(void (*callback)(void)); 237 | 238 | 239 | /* Set the callback for informing of a large PC change. 240 | * You must enable M68K_MONITOR_PC in m68kconf.h. 241 | * The CPU calls this callback with the new PC value every time the PC changes 242 | * by a large value (currently set for changes by longwords). 243 | * Default behavior: do nothing. 244 | */ 245 | void m68k_set_pc_changed_callback(void (*callback)(unsigned int new_pc)); 246 | 247 | 248 | /* Set the callback for CPU function code changes. 249 | * You must enable M68K_EMULATE_FC in m68kconf.h. 250 | * The CPU calls this callback with the function code before every memory 251 | * access to set the CPU's function code according to what kind of memory 252 | * access it is (supervisor/user, program/data and such). 253 | * Default behavior: do nothing. 254 | */ 255 | void m68k_set_fc_callback(void (*callback)(unsigned int new_fc)); 256 | 257 | 258 | /* Set a callback for the instruction cycle of the CPU. 259 | * You must enable M68K_INSTRUCTION_HOOK in m68kconf.h. 260 | * The CPU calls this callback just before fetching the opcode in the 261 | * instruction cycle. 262 | * Default behavior: do nothing. 263 | */ 264 | void m68k_set_instr_hook_callback(void (*callback)(void)); 265 | 266 | 267 | 268 | /* ======================================================================== */ 269 | /* ====================== FUNCTIONS TO ACCESS THE CPU ===================== */ 270 | /* ======================================================================== */ 271 | 272 | /* Use this function to set the CPU type you want to emulate. 273 | * Currently supported types are: M68K_CPU_TYPE_68000, M68K_CPU_TYPE_68010, 274 | * M68K_CPU_TYPE_EC020, and M68K_CPU_TYPE_68020. 275 | */ 276 | void m68k_set_cpu_type(unsigned int cpu_type); 277 | 278 | /* Do whatever initialisations the core requires. Should be called 279 | * at least once at init time. 280 | */ 281 | void m68k_init(void); 282 | 283 | /* Pulse the RESET pin on the CPU. 284 | * You *MUST* reset the CPU at least once to initialize the emulation 285 | * Note: If you didn't call m68k_set_cpu_type() before resetting 286 | * the CPU for the first time, the CPU will be set to 287 | * M68K_CPU_TYPE_68000. 288 | */ 289 | void m68k_pulse_reset(void); 290 | 291 | /* execute num_cycles worth of instructions. returns number of cycles used */ 292 | int m68k_execute(int num_cycles); 293 | 294 | /* These functions let you read/write/modify the number of cycles left to run 295 | * while m68k_execute() is running. 296 | * These are useful if the 68k accesses a memory-mapped port on another device 297 | * that requires immediate processing by another CPU. 298 | */ 299 | int m68k_cycles_run(void); /* Number of cycles run so far */ 300 | int m68k_cycles_remaining(void); /* Number of cycles left */ 301 | void m68k_modify_timeslice(int cycles); /* Modify cycles left */ 302 | void m68k_end_timeslice(void); /* End timeslice now */ 303 | 304 | /* Set the IPL0-IPL2 pins on the CPU (IRQ). 305 | * A transition from < 7 to 7 will cause a non-maskable interrupt (NMI). 306 | * Setting IRQ to 0 will clear an interrupt request. 307 | */ 308 | void m68k_set_irq(unsigned int int_level); 309 | 310 | 311 | /* Halt the CPU as if you pulsed the HALT pin. */ 312 | void m68k_pulse_halt(void); 313 | 314 | 315 | /* Context switching to allow multiple CPUs */ 316 | 317 | /* Get the size of the cpu context in bytes */ 318 | unsigned int m68k_context_size(void); 319 | 320 | /* Get a cpu context */ 321 | unsigned int m68k_get_context(void* dst); 322 | 323 | /* set the current cpu context */ 324 | void m68k_set_context(void* dst); 325 | 326 | /* Register the CPU state information */ 327 | void m68k_state_register(const char *type); 328 | 329 | 330 | /* Peek at the internals of a CPU context. This can either be a context 331 | * retrieved using m68k_get_context() or the currently running context. 332 | * If context is NULL, the currently running CPU context will be used. 333 | */ 334 | unsigned int m68k_get_reg(void* context, m68k_register_t reg); 335 | 336 | /* Poke values into the internals of the currently running CPU context */ 337 | void m68k_set_reg(m68k_register_t reg, unsigned int value); 338 | 339 | /* Check if an instruction is valid for the specified CPU type */ 340 | unsigned int m68k_is_valid_instruction(unsigned int instruction, unsigned int cpu_type); 341 | 342 | /* Disassemble 1 instruction using the epecified CPU type at pc. Stores 343 | * disassembly in str_buff and returns the size of the instruction in bytes. 344 | */ 345 | unsigned int m68k_disassemble(char* str_buff, unsigned int pc, unsigned int cpu_type); 346 | 347 | 348 | /* ======================================================================== */ 349 | /* ============================== MAME STUFF ============================== */ 350 | /* ======================================================================== */ 351 | 352 | #if M68K_COMPILE_FOR_MAME == OPT_ON 353 | #include "m68kmame.h" 354 | #endif /* M68K_COMPILE_FOR_MAME */ 355 | 356 | 357 | /* ======================================================================== */ 358 | /* ============================== END OF FILE ============================= */ 359 | /* ======================================================================== */ 360 | 361 | #endif /* M68K__HEADER */ 362 | -------------------------------------------------------------------------------- /mushashi/example/sim.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "sim.h" 6 | #include "m68k.h" 7 | #include "osd.h" 8 | 9 | void disassemble_program(); 10 | 11 | /* Memory-mapped IO ports */ 12 | #define INPUT_ADDRESS 0x800000 13 | #define OUTPUT_ADDRESS 0x400000 14 | 15 | /* IRQ connections */ 16 | #define IRQ_NMI_DEVICE 7 17 | #define IRQ_INPUT_DEVICE 2 18 | #define IRQ_OUTPUT_DEVICE 1 19 | 20 | /* Time between characters sent to output device (seconds) */ 21 | #define OUTPUT_DEVICE_PERIOD 1 22 | 23 | /* ROM and RAM sizes */ 24 | #define MAX_ROM 0xfff 25 | #define MAX_RAM 0xff 26 | 27 | 28 | /* Read/write macros */ 29 | #define READ_BYTE(BASE, ADDR) (BASE)[ADDR] 30 | #define READ_WORD(BASE, ADDR) (((BASE)[ADDR]<<8) | \ 31 | (BASE)[(ADDR)+1]) 32 | #define READ_LONG(BASE, ADDR) (((BASE)[ADDR]<<24) | \ 33 | ((BASE)[(ADDR)+1]<<16) | \ 34 | ((BASE)[(ADDR)+2]<<8) | \ 35 | (BASE)[(ADDR)+3]) 36 | 37 | #define WRITE_BYTE(BASE, ADDR, VAL) (BASE)[ADDR] = (VAL)&0xff 38 | #define WRITE_WORD(BASE, ADDR, VAL) (BASE)[ADDR] = ((VAL)>>8) & 0xff; \ 39 | (BASE)[(ADDR)+1] = (VAL)&0xff 40 | #define WRITE_LONG(BASE, ADDR, VAL) (BASE)[ADDR] = ((VAL)>>24) & 0xff; \ 41 | (BASE)[(ADDR)+1] = ((VAL)>>16)&0xff; \ 42 | (BASE)[(ADDR)+2] = ((VAL)>>8)&0xff; \ 43 | (BASE)[(ADDR)+3] = (VAL)&0xff 44 | 45 | 46 | /* Prototypes */ 47 | void exit_error(char* fmt, ...); 48 | 49 | unsigned int cpu_read_byte(unsigned int address); 50 | unsigned int cpu_read_word(unsigned int address); 51 | unsigned int cpu_read_long(unsigned int address); 52 | void cpu_write_byte(unsigned int address, unsigned int value); 53 | void cpu_write_word(unsigned int address, unsigned int value); 54 | void cpu_write_long(unsigned int address, unsigned int value); 55 | void cpu_pulse_reset(void); 56 | void cpu_set_fc(unsigned int fc); 57 | int cpu_irq_ack(int level); 58 | 59 | void nmi_device_reset(void); 60 | void nmi_device_update(void); 61 | int nmi_device_ack(void); 62 | 63 | void input_device_reset(void); 64 | void input_device_update(void); 65 | int input_device_ack(void); 66 | unsigned int input_device_read(void); 67 | void input_device_write(unsigned int value); 68 | 69 | void output_device_reset(void); 70 | void output_device_update(void); 71 | int output_device_ack(void); 72 | unsigned int output_device_read(void); 73 | void output_device_write(unsigned int value); 74 | 75 | void int_controller_set(unsigned int value); 76 | void int_controller_clear(unsigned int value); 77 | 78 | void get_user_input(void); 79 | 80 | 81 | /* Data */ 82 | unsigned int g_quit = 0; /* 1 if we want to quit */ 83 | unsigned int g_nmi = 0; /* 1 if nmi pending */ 84 | 85 | int g_input_device_value = -1; /* Current value in input device */ 86 | 87 | unsigned int g_output_device_ready = 0; /* 1 if output device is ready */ 88 | time_t g_output_device_last_output; /* Time of last char output */ 89 | 90 | unsigned int g_int_controller_pending = 0; /* list of pending interrupts */ 91 | unsigned int g_int_controller_highest_int = 0; /* Highest pending interrupt */ 92 | 93 | unsigned char g_rom[MAX_ROM+1]; /* ROM */ 94 | unsigned char g_ram[MAX_RAM+1]; /* RAM */ 95 | unsigned int g_fc; /* Current function code from CPU */ 96 | 97 | 98 | /* Exit with an error message. Use printf syntax. */ 99 | void exit_error(char* fmt, ...) 100 | { 101 | static int guard_val = 0; 102 | char buff[100]; 103 | unsigned int pc; 104 | va_list args; 105 | 106 | if(guard_val) 107 | return; 108 | else 109 | guard_val = 1; 110 | 111 | va_start(args, fmt); 112 | vfprintf(stderr, fmt, args); 113 | va_end(args); 114 | fprintf(stderr, "\n"); 115 | pc = m68k_get_reg(NULL, M68K_REG_PPC); 116 | m68k_disassemble(buff, pc, M68K_CPU_TYPE_68000); 117 | fprintf(stderr, "At %04x: %s\n", pc, buff); 118 | 119 | exit(EXIT_FAILURE); 120 | } 121 | 122 | 123 | /* Read data from RAM, ROM, or a device */ 124 | unsigned int cpu_read_byte(unsigned int address) 125 | { 126 | if(g_fc & 2) /* Program */ 127 | { 128 | if(address > MAX_ROM) 129 | exit_error("Attempted to read byte from ROM address %08x", address); 130 | return READ_BYTE(g_rom, address); 131 | } 132 | 133 | /* Otherwise it's data space */ 134 | switch(address) 135 | { 136 | case INPUT_ADDRESS: 137 | return input_device_read(); 138 | case OUTPUT_ADDRESS: 139 | return output_device_read(); 140 | default: 141 | break; 142 | } 143 | if(address > MAX_RAM) 144 | exit_error("Attempted to read byte from RAM address %08x", address); 145 | return READ_BYTE(g_ram, address); 146 | } 147 | 148 | unsigned int cpu_read_word(unsigned int address) 149 | { 150 | if(g_fc & 2) /* Program */ 151 | { 152 | if(address > MAX_ROM) 153 | exit_error("Attempted to read word from ROM address %08x", address); 154 | return READ_WORD(g_rom, address); 155 | } 156 | 157 | /* Otherwise it's data space */ 158 | switch(address) 159 | { 160 | case INPUT_ADDRESS: 161 | return input_device_read(); 162 | case OUTPUT_ADDRESS: 163 | return output_device_read(); 164 | default: 165 | break; 166 | } 167 | if(address > MAX_RAM) 168 | exit_error("Attempted to read word from RAM address %08x", address); 169 | return READ_WORD(g_ram, address); 170 | } 171 | 172 | unsigned int cpu_read_long(unsigned int address) 173 | { 174 | if(g_fc & 2) /* Program */ 175 | { 176 | if(address > MAX_ROM) 177 | exit_error("Attempted to read long from ROM address %08x", address); 178 | return READ_LONG(g_rom, address); 179 | } 180 | 181 | /* Otherwise it's data space */ 182 | switch(address) 183 | { 184 | case INPUT_ADDRESS: 185 | return input_device_read(); 186 | case OUTPUT_ADDRESS: 187 | return output_device_read(); 188 | default: 189 | break; 190 | } 191 | if(address > MAX_RAM) 192 | exit_error("Attempted to read long from RAM address %08x", address); 193 | return READ_LONG(g_ram, address); 194 | } 195 | 196 | 197 | unsigned int cpu_read_word_dasm(unsigned int address) 198 | { 199 | if(address > MAX_ROM) 200 | exit_error("Disassembler attempted to read word from ROM address %08x", address); 201 | return READ_WORD(g_rom, address); 202 | } 203 | 204 | unsigned int cpu_read_long_dasm(unsigned int address) 205 | { 206 | if(address > MAX_ROM) 207 | exit_error("Dasm attempted to read long from ROM address %08x", address); 208 | return READ_LONG(g_rom, address); 209 | } 210 | 211 | 212 | /* Write data to RAM or a device */ 213 | void cpu_write_byte(unsigned int address, unsigned int value) 214 | { 215 | if(g_fc & 2) /* Program */ 216 | exit_error("Attempted to write %02x to ROM address %08x", value&0xff, address); 217 | 218 | /* Otherwise it's data space */ 219 | switch(address) 220 | { 221 | case INPUT_ADDRESS: 222 | input_device_write(value&0xff); 223 | return; 224 | case OUTPUT_ADDRESS: 225 | output_device_write(value&0xff); 226 | return; 227 | default: 228 | break; 229 | } 230 | if(address > MAX_RAM) 231 | exit_error("Attempted to write %02x to RAM address %08x", value&0xff, address); 232 | WRITE_BYTE(g_ram, address, value); 233 | } 234 | 235 | void cpu_write_word(unsigned int address, unsigned int value) 236 | { 237 | if(g_fc & 2) /* Program */ 238 | exit_error("Attempted to write %04x to ROM address %08x", value&0xffff, address); 239 | 240 | /* Otherwise it's data space */ 241 | switch(address) 242 | { 243 | case INPUT_ADDRESS: 244 | input_device_write(value&0xffff); 245 | return; 246 | case OUTPUT_ADDRESS: 247 | output_device_write(value&0xffff); 248 | return; 249 | default: 250 | break; 251 | } 252 | if(address > MAX_RAM) 253 | exit_error("Attempted to write %04x to RAM address %08x", value&0xffff, address); 254 | WRITE_WORD(g_ram, address, value); 255 | } 256 | 257 | void cpu_write_long(unsigned int address, unsigned int value) 258 | { 259 | if(g_fc & 2) /* Program */ 260 | exit_error("Attempted to write %08x to ROM address %08x", value, address); 261 | 262 | /* Otherwise it's data space */ 263 | switch(address) 264 | { 265 | case INPUT_ADDRESS: 266 | input_device_write(value); 267 | return; 268 | case OUTPUT_ADDRESS: 269 | output_device_write(value); 270 | return; 271 | default: 272 | break; 273 | } 274 | if(address > MAX_RAM) 275 | exit_error("Attempted to write %08x to RAM address %08x", value, address); 276 | WRITE_LONG(g_ram, address, value); 277 | } 278 | 279 | /* Called when the CPU pulses the RESET line */ 280 | void cpu_pulse_reset(void) 281 | { 282 | nmi_device_reset(); 283 | output_device_reset(); 284 | input_device_reset(); 285 | } 286 | 287 | /* Called when the CPU changes the function code pins */ 288 | void cpu_set_fc(unsigned int fc) 289 | { 290 | g_fc = fc; 291 | } 292 | 293 | /* Called when the CPU acknowledges an interrupt */ 294 | int cpu_irq_ack(int level) 295 | { 296 | switch(level) 297 | { 298 | case IRQ_NMI_DEVICE: 299 | return nmi_device_ack(); 300 | case IRQ_INPUT_DEVICE: 301 | return input_device_ack(); 302 | case IRQ_OUTPUT_DEVICE: 303 | return output_device_ack(); 304 | } 305 | return M68K_INT_ACK_SPURIOUS; 306 | } 307 | 308 | 309 | 310 | 311 | /* Implementation for the NMI device */ 312 | void nmi_device_reset(void) 313 | { 314 | g_nmi = 0; 315 | } 316 | 317 | void nmi_device_update(void) 318 | { 319 | if(g_nmi) 320 | { 321 | g_nmi = 0; 322 | int_controller_set(IRQ_NMI_DEVICE); 323 | } 324 | } 325 | 326 | int nmi_device_ack(void) 327 | { 328 | printf("\nNMI\n");fflush(stdout); 329 | int_controller_clear(IRQ_NMI_DEVICE); 330 | return M68K_INT_ACK_AUTOVECTOR; 331 | } 332 | 333 | 334 | /* Implementation for the input device */ 335 | void input_device_reset(void) 336 | { 337 | g_input_device_value = -1; 338 | int_controller_clear(IRQ_INPUT_DEVICE); 339 | } 340 | 341 | void input_device_update(void) 342 | { 343 | if(g_input_device_value >= 0) 344 | int_controller_set(IRQ_INPUT_DEVICE); 345 | } 346 | 347 | int input_device_ack(void) 348 | { 349 | return M68K_INT_ACK_AUTOVECTOR; 350 | } 351 | 352 | unsigned int input_device_read(void) 353 | { 354 | int value = g_input_device_value > 0 ? g_input_device_value : 0; 355 | int_controller_clear(IRQ_INPUT_DEVICE); 356 | g_input_device_value = -1; 357 | return value; 358 | } 359 | 360 | void input_device_write(unsigned int value) 361 | { 362 | } 363 | 364 | 365 | /* Implementation for the output device */ 366 | void output_device_reset(void) 367 | { 368 | g_output_device_last_output = time(NULL); 369 | g_output_device_ready = 0; 370 | int_controller_clear(IRQ_OUTPUT_DEVICE); 371 | } 372 | 373 | void output_device_update(void) 374 | { 375 | if(!g_output_device_ready) 376 | { 377 | if((time(NULL) - g_output_device_last_output) >= OUTPUT_DEVICE_PERIOD) 378 | { 379 | g_output_device_ready = 1; 380 | int_controller_set(IRQ_OUTPUT_DEVICE); 381 | } 382 | } 383 | } 384 | 385 | int output_device_ack(void) 386 | { 387 | return M68K_INT_ACK_AUTOVECTOR; 388 | } 389 | 390 | unsigned int output_device_read(void) 391 | { 392 | int_controller_clear(IRQ_OUTPUT_DEVICE); 393 | return 0; 394 | } 395 | 396 | void output_device_write(unsigned int value) 397 | { 398 | char ch; 399 | if(g_output_device_ready) 400 | { 401 | ch = value & 0xff; 402 | printf("%c", ch); 403 | g_output_device_last_output = time(NULL); 404 | g_output_device_ready = 0; 405 | int_controller_clear(IRQ_OUTPUT_DEVICE); 406 | } 407 | } 408 | 409 | 410 | /* Implementation for the interrupt controller */ 411 | void int_controller_set(unsigned int value) 412 | { 413 | unsigned int old_pending = g_int_controller_pending; 414 | 415 | g_int_controller_pending |= (1< g_int_controller_highest_int) 418 | { 419 | g_int_controller_highest_int = value; 420 | m68k_set_irq(g_int_controller_highest_int); 421 | } 422 | } 423 | 424 | void int_controller_clear(unsigned int value) 425 | { 426 | g_int_controller_pending &= ~(1< 0;g_int_controller_highest_int--) 429 | if(g_int_controller_pending & (1<= 0) 443 | { 444 | switch(ch) 445 | { 446 | case 0x1b: 447 | g_quit = 1; 448 | break; 449 | case '~': 450 | if(last_ch != ch) 451 | g_nmi = 1; 452 | break; 453 | default: 454 | g_input_device_value = ch; 455 | } 456 | } 457 | last_ch = ch; 458 | } 459 | 460 | /* Disassembler */ 461 | void make_hex(char* buff, unsigned int pc, unsigned int length) 462 | { 463 | char* ptr = buff; 464 | 465 | for(;length>0;length -= 2) 466 | { 467 | sprintf(ptr, "%04x", cpu_read_word_dasm(pc)); 468 | pc += 2; 469 | ptr += 4; 470 | if(length > 2) 471 | *ptr++ = ' '; 472 | } 473 | } 474 | 475 | void disassemble_program() 476 | { 477 | unsigned int pc; 478 | unsigned int instr_size; 479 | char buff[100]; 480 | char buff2[100]; 481 | 482 | pc = cpu_read_long_dasm(4); 483 | 484 | while(pc <= 0x16e) 485 | { 486 | instr_size = m68k_disassemble(buff, pc, M68K_CPU_TYPE_68000); 487 | make_hex(buff2, pc, instr_size); 488 | printf("%03x: %-20s: %s\n", pc, buff2, buff); 489 | pc += instr_size; 490 | } 491 | fflush(stdout); 492 | } 493 | 494 | void cpu_instr_callback() 495 | { 496 | /* The following code would print out instructions as they are executed */ 497 | /* 498 | static char buff[100]; 499 | static char buff2[100]; 500 | static unsigned int pc; 501 | static unsigned int instr_size; 502 | 503 | pc = m68k_get_reg(NULL, M68K_REG_PC); 504 | instr_size = m68k_disassemble(buff, pc, M68K_CPU_TYPE_68000); 505 | make_hex(buff2, pc, instr_size); 506 | printf("E %03x: %-20s: %s\n", pc, buff2, buff); 507 | fflush(stdout); 508 | */ 509 | } 510 | 511 | 512 | 513 | /* The main loop */ 514 | int main(int argc, char* argv[]) 515 | { 516 | FILE* fhandle; 517 | 518 | if(argc != 2) 519 | { 520 | printf("Usage: sim \n"); 521 | exit(-1); 522 | } 523 | 524 | if((fhandle = fopen(argv[1], "rb")) == NULL) 525 | exit_error("Unable to open %s", argv[1]); 526 | 527 | if(fread(g_rom, 1, MAX_ROM+1, fhandle) <= 0) 528 | exit_error("Error reading %s", argv[1]); 529 | 530 | // disassemble_program(); 531 | 532 | m68k_init(); 533 | m68k_set_cpu_type(M68K_CPU_TYPE_68000); 534 | m68k_pulse_reset(); 535 | input_device_reset(); 536 | output_device_reset(); 537 | nmi_device_reset(); 538 | 539 | g_quit = 0; 540 | while(!g_quit) 541 | { 542 | // Our loop requires some interleaving to allow us to update the 543 | // input, output, and nmi devices. 544 | 545 | get_user_input(); 546 | 547 | // Values to execute determine the interleave rate. 548 | // Smaller values allow for more accurate interleaving with multiple 549 | // devices/CPUs but is more processor intensive. 550 | // 100000 is usually a good value to start at, then work from there. 551 | 552 | // Note that I am not emulating the correct clock speed! 553 | m68k_execute(100000); 554 | output_device_update(); 555 | input_device_update(); 556 | nmi_device_update(); 557 | } 558 | 559 | return 0; 560 | } 561 | 562 | --------------------------------------------------------------------------------