├── .gitignore ├── .vscode ├── c_cpp_properties.json └── settings.json ├── LICENSE ├── Makefile ├── README.md ├── device.c └── images ├── VSCode_on_linux.jpg ├── VSCode_on_windows_with_MSYS2.jpg ├── copy_driver_to_devs_with_dopus.jpg ├── disassembly_screenshot.jpg ├── mapping_device_name_in device_list.jpg ├── project_folder_mapped_as_drive_in_WinUAE.jpg ├── project_folder_shown_in_dopus.jpg ├── telnet_kprintf_output.jpg └── telnet_to_WinUAE_emulated_serial_port.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | build-debug 2 | build-release 3 | Thumbs.db 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Amiga", 5 | "includePath": [ 6 | //paths for the IntelliSense engine to use while searching for included headers. 7 | "${workspaceFolder}/**","C:\\msys64\\opt\\amiga\\m68k-amigaos\\ndk-include" 8 | //"C:\\msys64\\opt\\amiga\\m68k-amigaos\\ndk13-include" 9 | //"C:\\Users\\Jorgen\\.vscode\\extensions\\bartmanabyss.amiga-debug-1.1.0-preview31\\bin\\opt\\m68k-amiga-elf\\sys-include" 10 | ], 11 | "defines": [ 12 | //"__GNUC__=8", 13 | "__GNUC__=6", 14 | "_NO_INLINE" 15 | ], 16 | //Full path of the compiler being used to enable more accurate IntelliSense. 17 | "compilerPath": "C:\\msys64\\opt\\amiga\\bin\\m68k-amigaos-gcc.exe", 18 | //"compilerPath": "C:\\Users\\Jorgen\\.vscode\\extensions\\bartmanabyss.amiga-debug-1.1.0-preview31\\bin\\opt\\bin\\m68k-amiga-elf-gcc.exe", 19 | "cStandard": "c11", 20 | "cppStandard": "c++17", 21 | "intelliSenseMode": "gcc-x64" 22 | } 23 | ], 24 | "version": 4 25 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "telemetry.enableCrashReporter": false, 3 | "telemetry.enableTelemetry": false, 4 | "terminal.integrated.shell.windows": "C:\\msys64\\usr\\bin\\bash.exe", 5 | "terminal.integrated.shellArgs.windows": [ 6 | "--login", 7 | ], 8 | "terminal.integrated.env.windows": { 9 | "CHERE_INVOKING": "1", 10 | "MSYSTEM": "MINGW64", 11 | "MSYS2_PATH_TYPE": "inherit", 12 | "PATH": "/c/msys64/opt/amiga/bin:/c/Users/Jorgen/.vscode/extensions/bartmanabyss.amiga-debug-1.1.0-preview31/bin:/c/Users/Jorgen/.vscode/extensions/bartmanabyss.amiga-debug-1.1.0-preview31/bin/opt/bin", 13 | }, 14 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Jorgen Bilander 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | FILENAME=simple.device 2 | RELDIR=build-release 3 | DEBUGDIR=build-debug 4 | OBJECTS=device.o 5 | 6 | SRCDIRS=. 7 | INCDIRS=. 8 | 9 | #TARGET = elf-toolchain 10 | TARGET = hunk-toolchain 11 | 12 | ifeq ($(MAKECMDGOALS), debug) 13 | DIR=$(DEBUGDIR) 14 | EXTRA_CFLAGS+= -g -O2 15 | EXTRA_ASFLAGS+= -g 16 | ifeq ($(TARGET), hunk-toolchain) 17 | EXTRA_CFLAGS+= -DDEBUG -mcrt=clib2 18 | EXTRA_LDFLAGS+= -ldebug 19 | endif 20 | else 21 | DIR=$(RELDIR) 22 | EXTRA_CFLAGS+= -s -O2 23 | endif 24 | 25 | ifeq ($(TARGET), elf-toolchain) 26 | CC=m68k-amiga-elf-gcc 27 | AS=m68k-amiga-elf-as 28 | ELF_SUFFIX=.elf 29 | EXTRA_LDFLAGS+= -Wl,--emit-relocs,-Ttext=0,-Map=$(DIR)/$(FILENAME)$(ELF_SUFFIX).map 30 | endif 31 | 32 | ifeq ($(TARGET), hunk-toolchain) 33 | CC=m68k-amigaos-gcc 34 | AS=m68k-amigaos-as 35 | EXTRA_LDFLAGS+= -Wl,-Map=$(DIR)/$(FILENAME).map 36 | endif 37 | 38 | CFLAGS+= -m68000 -Wall -Wextra -Wno-unused-parameter -fomit-frame-pointer 39 | ASFLAGS+= -m68000 40 | LDFLAGS+= -nostdlib -nostartfiles 41 | 42 | OBJS:=$(addprefix $(DIR)/,$(OBJECTS)) 43 | 44 | CFLAGS+=$(addprefix -I,$(INCDIRS)) $(EXTRA_CFLAGS) 45 | ASFLAGS+=$(EXTRA_ASFLAGS) 46 | LDFLAGS+=$(EXTRA_LDFLAGS) 47 | 48 | # Search paths 49 | vpath %.c $(SRCDIRS) 50 | vpath %.s $(SRCDIRS) 51 | 52 | release: $(TARGET) 53 | debug: $(TARGET) 54 | 55 | elf-toolchain: $(DIR) $(OBJS) $(DIR)/$(FILENAME) 56 | elf2hunk $(DIR)/$(FILENAME)$(ELF_SUFFIX) $(DIR)/$(FILENAME) 57 | m68k-amiga-elf-objdump -D $(DIR)/$(FILENAME)$(ELF_SUFFIX) > $(DIR)/$(FILENAME)$(ELF_SUFFIX).s 58 | # m68k-amigaos-objdump -D $(DIR)/$(FILENAME) > $(DIR)/$(FILENAME).s 59 | 60 | $(DIR)/$(FILENAME): #ELF_SUFFIX is empty for hunk-toolchain so this below works for both targets 61 | $(CC) $(CFLAGS) -o $@$(ELF_SUFFIX) $(OBJS) $(LDFLAGS) 62 | 63 | hunk-toolchain: $(DIR) $(OBJS) $(DIR)/$(FILENAME) 64 | m68k-amigaos-objdump -D $(DIR)/$(FILENAME) > $(DIR)/$(FILENAME).s 65 | 66 | $(DIR): 67 | @mkdir $(DIR) 68 | 69 | $(DIR)/%.o: %.c 70 | $(CC) $(CFLAGS) -c -o $@ $< 71 | @$(DEL_EXE) 72 | 73 | $(DIR)/%.o: %.s 74 | $(AS) $(ASFLAGS) -o $@ $< 75 | @$(DEL_EXE) 76 | 77 | 78 | ifeq '$(findstring ;,$(PATH))' ';' 79 | UNAME := Windows_Native 80 | endif 81 | 82 | ifeq ($(UNAME),Windows_Native) 83 | RM= rmdir /s /q 84 | DEVNULL= 2>nul || ver>nul 85 | DEL_EXE=if exist $(DIR)\$(FILENAME) del /q $(DIR)\$(FILENAME) 86 | else 87 | RM= rm -rf 88 | DEL_EXE=test -f && $(RM) $(DIR)/$(FILENAME) 89 | endif 90 | 91 | .PHONY: clean 92 | 93 | clean: cleandebug 94 | clean: cleanrelease 95 | 96 | cleandebug: 97 | $(info Cleaning debug) 98 | @$(RM) $(DEBUGDIR) $(DEVNULL) 99 | 100 | cleanrelease: 101 | $(info Cleaning release) 102 | @$(RM) $(RELDIR) $(DEVNULL) 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Amiga SimpleDevice 2 | Amiga Simple Device driver skel in C using Bebbo's or Bartman's gcc. 3 | *** 4 | This example shows you how to build a simple device driver for the Amiga in C with a modern cross compile gcc-toolchain for the Amiga using VSCode as IDE. Driver should work with 1.3 and above. I haven't tested with older versions. 5 | *** 6 | The `Makefile` is made to work with both `Bebbo's amiga-gcc toolchain` as well as `Bartman^Abyss' amiga-elf gcc toolchain`. Currently it is set by default to Bebbo's toolchain, but you can easily switch by commenting out `hunk-toolchain` and uncomment `elf-toolchain` in the Makefile. Obviously you need to install "your" prefered toolchain for it to work and set the correct path to compiler and some settings in .vscode for intellisense and compilation to work. The Makefile should also work with Bartman's `gnumake.exe` running natively on Windows as well as `make` working on MSYS2 and on Linux. The `device.c` is only made to compile with Bebbo's toolchain since it is using register assignments to function parameters, no such thing exists in the elf-toolchain but you can work around it using inline asm should you want to try out building with the elf-toolchain instead. The `Makefile` is also set to automatically disassemble the build so that the assembly generated can easily be inspected. If you are using the elf-toolchain and have Bebbo's toolchain installed as well you can uncomment the `m68k-amigaos-objdump -D $(DIR)/$(FILENAME) > $(DIR)/$(FILENAME).s` line and you will have an disassembly of the executable hunk-file generated after the `elf2hunk` step as well. 7 | *** 8 | The Makfile is set to delete and recompile the executable if any source files `(%.c or %.s)` have been touched before running `make`. Just comment out the `@$(DEL_EXE)` on both `%.c and %.s` if you don't want it to work this way. 9 | *** 10 | ### Some screenshots: 11 | On Windows with MSYS2: 12 |
13 | 14 | 15 | 16 |
17 | 18 | On Linux Mint: 19 |
20 | 21 | 22 | 23 | *** 24 | `settings.json` in .vscode for linux would be something like this: 25 | ``` 26 | { 27 | "terminal.integrated.env.linux": { 28 | "PATH": "/opt/amiga/bin:${env:PATH}" 29 | }, 30 | } 31 | ``` 32 | and `c_cpp_properties.json` something like this: 33 | ``` 34 | "includePath": [ 35 | "${workspaceFolder}/**","/opt/amiga/m68k-amigaos/ndk-include" 36 | ], 37 | "compilerPath": "/opt/amiga/bin/m68k-amigaos-gcc.exe", 38 | ``` 39 | *** 40 | 41 | ### Make commands: 42 | * build debug: `make debug` (This creates a folder `build-debug` with all the build-files in it, compiles with debug-flags set) 43 | * build release: `make` (This creates a folder `build-release` with all the build-files in it, compiles with release-flags set) 44 | * clean debug: `make cleandebug` 45 | * clean release: `make cleanrelease` 46 | * clean both: `make clean` 47 | 48 | *** 49 | 50 | ### "Debug" with KPrintF in methods running in Forbidden mode by Exec: 51 | As you might know `Init_Device/Open/Close/Expunge` runs single threaded by Exec, no other task is allowed to run in this mode so no writing to a dos-console possible as it would mean deadlock to occur, however with the `debug.lib (libdebug.a)` linked into our build (with `-ldebug -mcrt=clib2`) it is possible to get `KPrintF`-statements out on the serial port terminal. Good thing is WinUAE has an option to emulate the serial terminal of the Amiga and then we can use telnet to connect to it with the command `telnet localhost 1234` 52 | 53 | Enable WinUAE serial port on 1234, and open a cmd-prompt and write the telnet command (obviously you'll need telnet installed first via `Control Panel->Program and Features->Turn windows features on/off)`. 54 |
55 |
56 | 57 | 58 | 59 | *** 60 | For conveniency, do also map the Project folder in WinUAE so we easily can copy our newly built device driver over to the emulated Amiga: 61 |

62 | 63 | 64 | 65 | *** 66 | With dopus on the Amiga you can copy the `simple.device` into `devs`. 67 |

68 | 69 | 70 | 71 |

72 | 73 | 74 | 75 | *** 76 | To load the device we need a device list with the Device entry pointing to our name of the file `simple.device`, I'll use a `SD0`-file here made in `Devs` as a dummy to see if it will work to load the driver, Exec will scan for a romtag in our device driver: 77 |

78 | 79 | 80 | 81 | *** 82 | And with telnet connected and running by double-clicking the `SD0`-file it is indeed showing `KPrintF`-output on the serial console...yay! 83 |

84 | 85 | 86 | 87 | *** 88 | In the disassembly we can also se that the romtag is created correctly after `moveq #-1,d0` `rts` as it should be: 89 |

90 | 91 | 92 | 93 | *** 94 | That's all folks!, Happy Amiga Hackin' 95 | -------------------------------------------------------------------------------- /device.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #if DEBUG 7 | #include 8 | #endif 9 | 10 | #define STR(s) #s /* Turn s into a string literal without expanding macro definitions (however, \ 11 | if invoked from a macro, macro arguments are expanded). */ 12 | #define XSTR(s) STR(s) /* Turn s into a string literal after macro-expanding it. */ 13 | 14 | #define DEVICE_NAME "simple.device" 15 | #define DEVICE_DATE "(1 Sep 2020)" 16 | #define DEVICE_ID_STRING "simple " XSTR(DEVICE_VERSION) "." XSTR(DEVICE_REVISION) " " DEVICE_DATE /* format is: 'name version.revision (d.m.yy)' */ 17 | #define DEVICE_VERSION 1 18 | #define DEVICE_REVISION 0 19 | #define DEVICE_PRIORITY 0 /* Most people will not need a priority and should leave it at zero. */ 20 | 21 | struct ExecBase *SysBase; 22 | BPTR saved_seg_list; 23 | 24 | BOOL is_open; 25 | 26 | /*----------------------------------------------------------- 27 | A library or device with a romtag should start with moveq #-1,d0 (to 28 | safely return an error if a user tries to execute the file), followed by a 29 | Resident structure. 30 | ------------------------------------------------------------*/ 31 | int __attribute__((no_reorder)) _start() 32 | { 33 | return -1; 34 | } 35 | 36 | /*----------------------------------------------------------- 37 | A romtag structure. After your driver is brought in from disk, the 38 | disk image will be scanned for this structure to discover magic constants 39 | about you (such as where to start running you from...). 40 | 41 | endcode is a marker that shows the end of your code. Make sure it does not 42 | span hunks, and is not before the rom tag! It is ok to put it right after 43 | the rom tag -- that way you are always safe. 44 | Make sure your program has only a single code hunk if you put it at the 45 | end of your code. 46 | ------------------------------------------------------------*/ 47 | asm("romtag: \n" 48 | " dc.w "XSTR(RTC_MATCHWORD)" \n" 49 | " dc.l romtag \n" 50 | " dc.l endcode \n" 51 | " dc.b "XSTR(RTF_AUTOINIT)" \n" 52 | " dc.b "XSTR(DEVICE_VERSION)" \n" 53 | " dc.b "XSTR(NT_DEVICE)" \n" 54 | " dc.b "XSTR(DEVICE_PRIORITY)" \n" 55 | " dc.l _device_name \n" 56 | " dc.l _device_id_string \n" 57 | " dc.l _auto_init_tables \n" 58 | "endcode: \n"); 59 | 60 | char device_name[] = DEVICE_NAME; 61 | char device_id_string[] = DEVICE_ID_STRING; 62 | 63 | /*------- init_device --------------------------------------- 64 | FOR RTF_AUTOINIT: 65 | This routine gets called after the device has been allocated. 66 | The device pointer is in d0. The AmigaDOS segment list is in a0. 67 | If it returns the device pointer, then the device will be linked 68 | into the device list. If it returns NULL, then the device 69 | will be unloaded. 70 | 71 | IMPORTANT: 72 | If you don't use the "RTF_AUTOINIT" feature, there is an additional 73 | caveat. If you allocate memory in your Open function, remember that 74 | allocating memory can cause an Expunge... including an expunge of your 75 | device. This must not be fatal. The easy solution is don't add your 76 | device to the list until after it is ready for action. 77 | 78 | CAUTION: 79 | This function runs in a forbidden state !!! 80 | This call is single-threaded by Exec 81 | ------------------------------------------------------------*/ 82 | static struct Library __attribute__((used)) * init_device(BPTR seg_list asm("a0"), struct Library *dev asm("d0")) 83 | { 84 | #if DEBUG 85 | KPrintF((CONST_STRPTR) "running init_device()\n"); 86 | #endif 87 | 88 | /* !!! required !!! save a pointer to exec */ 89 | SysBase = *(struct ExecBase **)4UL; 90 | 91 | /* save pointer to our loaded code (the SegList) */ 92 | saved_seg_list = seg_list; 93 | 94 | dev->lib_Node.ln_Type = NT_DEVICE; 95 | dev->lib_Node.ln_Name = device_name; 96 | dev->lib_Flags = LIBF_SUMUSED | LIBF_CHANGED; 97 | dev->lib_Version = DEVICE_VERSION; 98 | dev->lib_Revision = DEVICE_REVISION; 99 | dev->lib_IdString = (APTR)device_id_string; 100 | 101 | is_open = FALSE; 102 | 103 | return dev; 104 | } 105 | 106 | /* device dependent expunge function 107 | !!! CAUTION: This function runs in a forbidden state !!! 108 | This call is guaranteed to be single-threaded; only one task 109 | will execute your Expunge at a time. */ 110 | static BPTR __attribute__((used)) expunge(struct Library *dev asm("a6")) 111 | { 112 | #if DEBUG 113 | KPrintF((CONST_STRPTR) "running expunge()\n"); 114 | #endif 115 | 116 | if (dev->lib_OpenCnt != 0) 117 | { 118 | dev->lib_Flags |= LIBF_DELEXP; 119 | return 0; 120 | } 121 | 122 | //xyz_shutdown(); 123 | 124 | BPTR seg_list = saved_seg_list; 125 | Remove(&dev->lib_Node); 126 | FreeMem((char *)dev - dev->lib_NegSize, dev->lib_NegSize + dev->lib_PosSize); 127 | return seg_list; 128 | } 129 | 130 | /* device dependent open function 131 | !!! CAUTION: This function runs in a forbidden state !!! 132 | This call is guaranteed to be single-threaded; only one task 133 | will execute your Open at a time. */ 134 | static void __attribute__((used)) open(struct Library *dev asm("a6"), struct IORequest *ioreq asm("a1"), ULONG unitnum asm("d0"), ULONG flags asm("d1")) 135 | { 136 | #if DEBUG 137 | KPrintF((CONST_STRPTR) "running open()\n"); 138 | #endif 139 | 140 | ioreq->io_Error = IOERR_OPENFAIL; 141 | ioreq->io_Message.mn_Node.ln_Type = NT_REPLYMSG; 142 | 143 | if (unitnum != 0) 144 | return; 145 | 146 | if (!is_open) 147 | { 148 | //initialize and open here 149 | 150 | //xyz_initialize(); 151 | //if (xy_open() != 0) 152 | // return; 153 | 154 | is_open = TRUE; 155 | } 156 | 157 | dev->lib_OpenCnt++; 158 | ioreq->io_Error = 0; //Success 159 | } 160 | 161 | /* device dependent close function 162 | !!! CAUTION: This function runs in a forbidden state !!! 163 | This call is guaranteed to be single-threaded; only one task 164 | will execute your Close at a time. */ 165 | static BPTR __attribute__((used)) close(struct Library *dev asm("a6"), struct IORequest *ioreq asm("a1")) 166 | { 167 | #if DEBUG 168 | KPrintF((CONST_STRPTR) "running close()\n"); 169 | #endif 170 | 171 | ioreq->io_Device = NULL; 172 | ioreq->io_Unit = NULL; 173 | 174 | dev->lib_OpenCnt--; 175 | 176 | if (dev->lib_OpenCnt == 0 && (dev->lib_Flags & LIBF_DELEXP)) 177 | return expunge(dev); 178 | 179 | return 0; 180 | } 181 | 182 | /* device dependent beginio function */ 183 | static void __attribute__((used)) begin_io(struct Library *dev asm("a6"), struct IORequest *ioreq asm("a1")) 184 | { 185 | #if DEBUG 186 | KPrintF((CONST_STRPTR) "running begin_io()\n"); 187 | #endif 188 | } 189 | 190 | /* device dependent abortio function */ 191 | static ULONG __attribute__((used)) abort_io(struct Library *dev asm("a6"), struct IORequest *ioreq asm("a1")) 192 | { 193 | #if DEBUG 194 | KPrintF((CONST_STRPTR) "running abort_io()\n"); 195 | #endif 196 | 197 | return IOERR_NOCMD; 198 | } 199 | 200 | static ULONG device_vectors[] = 201 | { 202 | (ULONG)open, 203 | (ULONG)close, 204 | (ULONG)expunge, 205 | 0, //extFunc not used here 206 | (ULONG)begin_io, 207 | (ULONG)abort_io, 208 | -1}; //function table end marker 209 | 210 | /*----------------------------------------------------------- 211 | The romtag specified that we were "RTF_AUTOINIT". This means 212 | that the RT_INIT structure member points to one of these 213 | tables below. If the AUTOINIT bit was not set then RT_INIT 214 | would point to a routine to run. 215 | 216 | MyDev_Sizeof data space size 217 | device_vectors pointer to function initializers 218 | dataTable pointer to data initializers 219 | init_device routine to run 220 | ------------------------------------------------------------*/ 221 | const ULONG auto_init_tables[4] = 222 | { 223 | sizeof(struct Library), 224 | (ULONG)device_vectors, 225 | 0, 226 | (ULONG)init_device}; -------------------------------------------------------------------------------- /images/VSCode_on_linux.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbilander/SimpleDevice/37a3f0dcc42e02da48195d8860cad6794db3d797/images/VSCode_on_linux.jpg -------------------------------------------------------------------------------- /images/VSCode_on_windows_with_MSYS2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbilander/SimpleDevice/37a3f0dcc42e02da48195d8860cad6794db3d797/images/VSCode_on_windows_with_MSYS2.jpg -------------------------------------------------------------------------------- /images/copy_driver_to_devs_with_dopus.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbilander/SimpleDevice/37a3f0dcc42e02da48195d8860cad6794db3d797/images/copy_driver_to_devs_with_dopus.jpg -------------------------------------------------------------------------------- /images/disassembly_screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbilander/SimpleDevice/37a3f0dcc42e02da48195d8860cad6794db3d797/images/disassembly_screenshot.jpg -------------------------------------------------------------------------------- /images/mapping_device_name_in device_list.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbilander/SimpleDevice/37a3f0dcc42e02da48195d8860cad6794db3d797/images/mapping_device_name_in device_list.jpg -------------------------------------------------------------------------------- /images/project_folder_mapped_as_drive_in_WinUAE.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbilander/SimpleDevice/37a3f0dcc42e02da48195d8860cad6794db3d797/images/project_folder_mapped_as_drive_in_WinUAE.jpg -------------------------------------------------------------------------------- /images/project_folder_shown_in_dopus.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbilander/SimpleDevice/37a3f0dcc42e02da48195d8860cad6794db3d797/images/project_folder_shown_in_dopus.jpg -------------------------------------------------------------------------------- /images/telnet_kprintf_output.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbilander/SimpleDevice/37a3f0dcc42e02da48195d8860cad6794db3d797/images/telnet_kprintf_output.jpg -------------------------------------------------------------------------------- /images/telnet_to_WinUAE_emulated_serial_port.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbilander/SimpleDevice/37a3f0dcc42e02da48195d8860cad6794db3d797/images/telnet_to_WinUAE_emulated_serial_port.jpg --------------------------------------------------------------------------------