├── README.md ├── crc.c ├── crc.h ├── ctype.h ├── disk.c ├── disk.h ├── license.txt ├── make.txt ├── mcopyata.c ├── mcopyata.h ├── mserial.c ├── mserial.h ├── portio.c ├── portio.h ├── sys.c └── sys.h /README.md: -------------------------------------------------------------------------------- 1 | # mcopyata 2 | #### Copy a floppy/hard drive from one computer to another via a serial null modem connection 3 | 4 | One day, a few years ago, I wanted to preserve the contents of the hard drive of an old, somewhat obscure machine. If it was a modern machine, I could have simply written a simple app (not counting the many I could have downloaded) to read each sector of the disk and save it to a USB thumb drive. Easy, right? 5 | 6 | Well, it wasn't a modern machine and no USB. With this in mind, if I read each sector and try to save it to the disk, the disk itself will fill up fast, let alone write to sectors I have already read. It doesn't have another Hard Drive Controller slot either, so no Slave drive...Where do I send the sector data? Also, it was an old enough machine and the drive didn't look like a standard IDE cabled drive, so I couldn't even pull it out and put it in the modern machine's empty HDC slot. 7 | 8 | I got out my Serial Port notes and wrote a small/quick app to send these sectors across a (null modem) serial line to another computer with a hard drive large enough to hold the disk image, which was easy to find since it was at most 20meg, if memory serves. 9 | 10 | Therefore, the code on this page was created. 11 | 12 | As long as you have a null modem serial cable (you can find one online for practically nothing) and each computer has a standard serial port, you can read sectors from one computer's drive and store them as an image file on another computer. 13 | 14 | For the receiver, I used a standard x86 desktop machine running Windows XP (with a DOS session), with the following command line: 15 | 16 | `mcopyata /r /c 1` 17 | 18 | For the sender, I used the following command line: 19 | 20 | `mcopyata /s /x 0 /l 205360 /d 0x80 /c 2 /M` 21 | 22 | In mcopyata.c, at the `parse command line` function, you will see the parameter meanings: 23 | 24 | ``` 25 | parse the command line 26 | usage: 27 | /s = sender 28 | /r = receiver 29 | /x [0x,0] = starting lba (if sender) value can be in hex or integer (required for sender, ignored if receiver) 30 | /l [0x,0] = count of sectors (if sender) value can be in hex or integer (required for sender, ignored if receiver) 31 | /c [0x,0] = com port number (required for both sender and receiver) (1, 2, 3, 4) 32 | /d [0x,0] = BIOS drive number 0x80, 0x81, etc. (required for sender, ignored if receiver) 33 | /M = transfer maximum amount of data each transfer (optional for sender, ignored if receiver) 34 | /m = transfer only one sector at a time (optional for sender, ignored if receiver) 35 | ``` 36 | 37 | This code is designed to read up to 127 sectors at a time sending the data across the line. It sends and receives 32-byte packets for services such as: `Okay, I'm ready`, `Okay, I will send a buffer of 'count' sectors`, `Okay, I received it, send the next`, etc. 38 | 39 | It needs work. For example, I don't check for timeouts (the machine will wait for a character indefinitely), as well as other errors. I only needed it to copy the whole drive. Once I did that, the task was over. No need to make it any more robust. 40 | 41 | If you use this code, you might need to slow down the baud-rate, transfer a sector at a time (the /m command parameter), and/or modify the code a little to get it to work for your particular situation. 42 | 43 | Of course since it uses the BIOS disk read services, you know that it is x86 real-mode only. More to the point, the sender must be 16-bit real-mode to be able to use the BIOS services. The reciever can in fact be 32-bit, or even 64-bit. However, here, the same code is compiled whether it is the sender or the receiver. If you modify the code a bit, you might be able to compile it, as the receiver, as a 64-bit console app on a different platform since it only needs to access the serial hardware and write to a disk file. 44 | 45 | For more information on why I did this, see the [Blog Post](https://www.fysnet.net/blog/2018/08/index.html#20aug2018) where I showcase this obscure computer. 46 | 47 | ``` 48 | /* Author: Benjamin David Lunt 49 | * Forever Young Software 50 | * Copyright (c) 1984-2018 51 | * 52 | * This code is donated to the Freeware communitee. You have the 53 | * right to use it for learning purposes only. You may not modify it 54 | * for redistribution for any other purpose unless you have written 55 | * permission from the author. 56 | * 57 | * You may modify and use it in your own projects as long as they are 58 | * for non-profit only and not distributed. Any project for profit that 59 | * uses this code must have written permission from the author. 60 | * 61 | * For more information: 62 | * http://www.fysnet.net/blog/2018/08/index.html#20aug2018 63 | * Contact: 64 | * fys [at] fysnet [dot] net 65 | * 66 | * Last update: 20 Aug 2018 67 | * 68 | * compile using SmallerC (https://github.com/alexfru/SmallerC/) 69 | * smlrcc @make.txt 70 | * 71 | * Notes: 72 | * - This hard codes to the maximum Serial BAUD rate. You may need to adjust 73 | * to a slower rate and recompile. 74 | * 75 | * Things TODO: 76 | * - actually check the return value of the serial_send() and serial_recv() 77 | * functions to see if we timed-out. 78 | * - along with this time-out thing, the check for time-out withint the two 79 | * functions will dramatically slow down the transfer rate. For now, we have 80 | * it commented out. 81 | * 82 | */ 83 | ``` 84 | -------------------------------------------------------------------------------- /crc.c: -------------------------------------------------------------------------------- 1 | 2 | /* Author: Benjamin David Lunt 3 | * Forever Young Software 4 | * Copyright (c) 1984-2018 5 | * 6 | * This code is donated to the Freeware communitee. You have the 7 | * right to use it for learning purposes only. You may not modify it 8 | * for redistribution for any other purpose unless you have written 9 | * permission from the author. 10 | * 11 | * You may modify and use it in your own projects as long as they are 12 | * for non-profit only and not distributed. Any project for profit that 13 | * uses this code must have written permission from the author. 14 | * 15 | * For more information: 16 | * http://www.fysnet.net/blog/2018/08/index.html#20aug2018 17 | * Contact: 18 | * fys [at] fysnet [dot] net 19 | * 20 | * Last update: 20 Aug 2018 21 | * 22 | * compile using SmallerC (https://github.com/alexfru/SmallerC/) 23 | * smlrcc @make.txt 24 | * 25 | */ 26 | 27 | #include "ctype.h" 28 | #include "crc.h" 29 | 30 | // simple sum crc of all dwords in buffer 31 | bit32u calc_crc(void *buffer, const int size) { 32 | bit32u crc = 0, *p = (bit32u *) buffer; 33 | int cnt = size / sizeof(bit32u); // truncate any non-dword sized buffers 34 | 35 | for (int i=0; i> 4) & 0xFFFF) 33 | #define FP_OFF(x) ((bit32u) (x) >> 20 << 4) | ((bit32u) (x) & 0xF) 34 | 35 | unsigned total_cyls; 36 | unsigned sec_per_track; 37 | unsigned heads; 38 | 39 | // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 40 | // This routine converts LBA to CHS 41 | // Sector = (LBA mod SPT) + 1 42 | // Head = (LBA / SPT) mod Heads 43 | // Cylinder = (LBA / SPT) / Heads 44 | // (SPT = Sectors per Track) 45 | void lba_to_chs(const bit32u lba, unsigned int *cyl, unsigned int *head, unsigned int *sect) { 46 | *sect = (lba % sec_per_track) + 1; 47 | *head = (lba / sec_per_track) % heads; 48 | *cyl = (lba / sec_per_track) / heads; 49 | } 50 | 51 | // returns sectors per track 52 | // or 53 | // -1 if error 54 | bit32u get_drv_parms(const bit32u drv_num) { 55 | struct REGS regs; 56 | 57 | regs.eax = 0x00000800; 58 | regs.edx = drv_num; 59 | regs.es = 0; 60 | regs.edi = 0; 61 | if (!intx(0x13, ®s)) { 62 | sec_per_track = regs.ecx & 0x3F; 63 | heads = ((regs.edx & 0xFF00) >> 8) + 1; 64 | printf("Found %li total cylinders\n", ((regs.ecx & 0xC0) << 2) | ((regs.ecx >> 8) & 0xFF)); 65 | return sec_per_track; 66 | } else { 67 | printf("Error retrieving drive information..."); 68 | return -1; 69 | } 70 | } 71 | 72 | bool read_sector(void *buffer, const bit32u lba, const int count, const bit32u drv_num) { 73 | struct REGS regs; 74 | unsigned cyl, head, sect; 75 | 76 | lba_to_chs(lba, &cyl, &head, §); 77 | 78 | regs.eax = 0x00000200 | (count & 0xFF); 79 | regs.ecx = ((cyl & 0xFF) << 8) | (((cyl >> 8) & 3) << 6) | (sect & 0x3F); // CH = low 8 bits of cyl, CL = CCSSSSSS 80 | regs.edx = (head << 8) | drv_num; 81 | regs.ebx = FP_OFF(buffer); 82 | regs.es = FP_SEG(buffer); 83 | return ((intx(0x13, ®s) == FALSE) && ((regs.eax & 0x0000FF00) == 0)); 84 | } 85 | -------------------------------------------------------------------------------- /disk.h: -------------------------------------------------------------------------------- 1 | 2 | /* Author: Benjamin David Lunt 3 | * Forever Young Software 4 | * Copyright (c) 1984-2018 5 | * 6 | * This code is donated to the Freeware communitee. You have the 7 | * right to use it for learning purposes only. You may not modify it 8 | * for redistribution for any other purpose unless you have written 9 | * permission from the author. 10 | * 11 | * You may modify and use it in your own projects as long as they are 12 | * for non-profit only and not distributed. Any project for profit that 13 | * uses this code must have written permission from the author. 14 | * 15 | * For more information: 16 | * http://www.fysnet.net/blog/2018/08/index.html#20aug2018 17 | * Contact: 18 | * fys [at] fysnet [dot] net 19 | * 20 | * Last update: 20 Aug 2018 21 | * 22 | * compile using SmallerC (https://github.com/alexfru/SmallerC/) 23 | * smlrcc @make.txt 24 | * 25 | */ 26 | 27 | #ifndef DISK_H 28 | #define DISK_H 29 | 30 | bit32u get_drv_parms(const bit32u drv_num); 31 | bool read_sector(void *buffer, const bit32u lba, const int count, const bit32u drv_num); 32 | 33 | 34 | 35 | #endif // DISK_H 36 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | 2 | Author: Benjamin David Lunt 3 | Forever Young Software 4 | Copyright (c) 1984-2018 5 | 6 | This code is donated to the Freeware communitee. You have the 7 | right to use it for learning purposes only. You may not modify it 8 | for redistribution for any other purpose unless you have written 9 | permission from the author. 10 | 11 | You may modify and use it in your own projects as long as they are 12 | for non-profit only and not distributed. Any project for profit that 13 | uses this code must have written permission from the author. 14 | 15 | For more information: 16 | http://www.fysnet.net/blog/2018/08/index.html#20aug2018 17 | Contact: 18 | fys [at] fysnet [dot] net 19 | -------------------------------------------------------------------------------- /make.txt: -------------------------------------------------------------------------------- 1 | -dosh -o mcopyata.exe -I . 2 | 3 | mcopyata.c 4 | 5 | crc.c 6 | disk.c 7 | mserial.c 8 | portio.c 9 | sys.c -------------------------------------------------------------------------------- /mcopyata.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | /* Author: Benjamin David Lunt 4 | * Forever Young Software 5 | * Copyright (c) 1984-2018 6 | * 7 | * This code is donated to the Freeware communitee. You have the 8 | * right to use it for learning purposes only. You may not modify it 9 | * for redistribution for any other purpose unless you have written 10 | * permission from the author. 11 | * 12 | * You may modify and use it in your own projects as long as they are 13 | * for non-profit only and not distributed. Any project for profit that 14 | * uses this code must have written permission from the author. 15 | * 16 | * For more information: 17 | * http://www.fysnet.net/blog/2018/08/index.html#20aug2018 18 | * Contact: 19 | * fys [at] fysnet [dot] net 20 | * 21 | * Last update: 20 Aug 2018 22 | * 23 | * compile using SmallerC (https://github.com/alexfru/SmallerC/) 24 | * smlrcc @make.txt 25 | * 26 | * Notes: 27 | * - This hard codes to the maximum Serial BAUD rate. You may need to adjust 28 | * to a slower rate and recompile. 29 | * 30 | * Things TODO: 31 | * - actually check the return value of the serial_send() and serial_recv() 32 | * functions to see if we timed-out. 33 | * - along with this time-out thing, the check for time-out withint the two 34 | * functions will dramatically slow down the transfer rate. For now, we have 35 | * it commented out. 36 | * 37 | */ 38 | 39 | #include 40 | 41 | #include "ctype.h" 42 | #include "mcopyata.h" 43 | 44 | #include "disk.h" 45 | #include "mserial.h" 46 | #include "portio.h" 47 | #include "sys.h" 48 | 49 | bool sender = FALSE; // TRUE if this is the sending side (we have both so we make sure only one is specified) 50 | bool receiver = FALSE; // TRUE if this is the receiver side (...) 51 | bit32u start_lba = 0; // starting lba of transfer 52 | bit32u lba_count = 1; // count of LBA's to transmit 53 | bit32u port_num = -1; // com 1, com 2, etc. 54 | bit32u drv_num = -1; // 0x00, 0x01, 0x80, or 0x81 55 | bool maximum = FALSE; // (if neither max or min specified, we do a track at a time) 56 | bool minimum = FALSE; // default 57 | 58 | unsigned port_addr; // port address 59 | 60 | int main(int argc, char *argv[]) { 61 | struct S_HEADER header; 62 | struct S_RESPONSE response; 63 | unsigned sec_per_track, count_per_transfer; 64 | void *buffer = NULL; 65 | FILE *fp; 66 | int ret = 0; 67 | 68 | if (!parse_cmnd_line(argc, argv)) 69 | return -1; 70 | 71 | // if the sender, get the disk parameters. 72 | // returning the sectors per track so that we can transfer 73 | // this amount at one time. 74 | if (sender) 75 | if ((sec_per_track = get_drv_parms(drv_num)) == -1) 76 | return -3; 77 | 78 | // default to a full track of sectors 79 | count_per_transfer = sec_per_track; 80 | 81 | // if min specified, do a sector at a time 82 | if (minimum) 83 | count_per_transfer = 1; // only do one sector at a time 84 | 85 | // if max specified, read as many tracks as we can per transfer 86 | // 0x7F sectors is the maximum older BIOSes will allow ?????? 87 | if (maximum) { 88 | count_per_transfer = sec_per_track; 89 | while ((count_per_transfer + sec_per_track) <= 0x7F) 90 | count_per_transfer += sec_per_track; 91 | } 92 | 93 | // initialize the serial port 94 | // Note: 95 | // If we transfer (512 + 32 + 32) = 576 bytes per sector transferred. 96 | // At this rate, we should transfer 100 sectors per second, not counting disk read, etc. 97 | // However, it is much slower due to everything that must happen between sector transfers (CRC checks, etc) 98 | if (!init_serial(port_addr, SER_BAUD_115200, SER_PARITY_NONE | SER_STOP_1 | SER_BITS_8)) { 99 | puts("Could not initialize serial port."); 100 | return -2; 101 | } 102 | 103 | // if the receiver, we need to create/open the disk image file 104 | if (receiver) { 105 | if ((fp = fopen("disk.img", "r+b")) == NULL) { // try to open it 106 | if ((fp = fopen("disk.img", "w+b")) == NULL) { // else, try to create it 107 | printf("Error opening/creating file....\n"); 108 | return -2; 109 | } 110 | } 111 | } else { 112 | // if the sender, we know the size of the buffer we need 113 | // if the receiver, we don't know it yet... 114 | buffer = (void *) malloc(count_per_transfer * 512); 115 | } 116 | 117 | serial_clear_buffer(port_addr); 118 | bit32u lba = start_lba; 119 | if (sender) { 120 | while (lba < (start_lba + lba_count)) { 121 | // if the user hits the ESC key, exit (informing the receiver) 122 | if (kbhit() && (getch() == 0x011B)) { // send abort header 123 | header.code = HDR_CODE_ABORT; 124 | break; 125 | } 126 | // don't go past the count specified 127 | bit32u count = (((start_lba + lba_count) - lba) < count_per_transfer) ? ((start_lba + lba_count) - lba) : count_per_transfer; 128 | if (read_sector(buffer, lba, count, drv_num)) { 129 | header.magic = HDR_MAGIC; 130 | header.lba = lba; 131 | header.count = count; 132 | header.code = HDR_CODE_GOOD; 133 | header.crc = calc_crc(buffer, count * 512); 134 | printf("Sending LBA %li -> %li\n", lba, lba + count - 1); 135 | serial_send(port_addr, &header, sizeof(struct S_HEADER), STD_TIMEOUT); 136 | serial_send(port_addr, buffer, count * 512, STD_TIMEOUT); 137 | } else { 138 | printf("Error reading from lba %li\n", lba); 139 | header.code = HDR_CODE_ABORT; 140 | break; 141 | } 142 | serial_recv(port_addr, &response, sizeof(struct S_RESPONSE), STD_TIMEOUT); 143 | if (response.resp_magic == RESP_MAGIC) { 144 | switch (response.response) { 145 | case RESP_GOOD: 146 | break; 147 | case RESP_ABORT: 148 | puts("Received abort response from receiver."); 149 | ret = -4; 150 | goto end_transfer; 151 | default: 152 | printf("Received bad response: 0x%08lX\n", response.response); 153 | ret = -5; 154 | goto end_transfer; 155 | } 156 | } else { 157 | printf("Did not receive response... (0x%08lX 0x%08lX)\n", response.resp_magic, response.response); 158 | header.code = HDR_CODE_ABORT; 159 | break; 160 | } 161 | lba += count; 162 | header.code = HDR_CODE_DONE; // in case we are done 163 | } 164 | // sending ending header 165 | header.magic = HDR_MAGIC; 166 | header.lba = 0; 167 | header.crc = 0; // only calculated for the disk buffer data 168 | serial_send(port_addr, &header, sizeof(struct S_HEADER), STD_TIMEOUT); 169 | } else { 170 | puts("Waiting for sender to connect."); 171 | // this waits indefinately for the sender... 172 | if (serial_recv(port_addr, &header, sizeof(struct S_HEADER), NO_TIMEOUT) == -1) { 173 | printf("Receiver timed out\n"); 174 | } else { 175 | // we didn't know the size of the buffer until now, so allocate it now. 176 | buffer = (void *) malloc(header.count * 512); 177 | while (header.code == HDR_CODE_GOOD) { 178 | serial_recv(port_addr, buffer, header.count * 512, STD_TIMEOUT); 179 | 180 | //printf("\rReceived lba %li ", header.lba); 181 | printf("Received buffer for lba %li -> %li\n", header.lba, header.lba + header.count - 1); 182 | 183 | // assume good response 184 | response.response = RESP_GOOD; 185 | if (header.crc != calc_crc(buffer, header.count * 512)) { 186 | puts("CRC does not compute..."); 187 | response.response = RESP_ABORT; 188 | } 189 | // if the user hits the ESC key, exit (informing the sender) 190 | if (kbhit() && (getch() == 0x011B)) // send abort header 191 | response.response = RESP_ABORT; 192 | response.resp_magic = RESP_MAGIC; 193 | serial_send(port_addr, &response, sizeof(struct S_RESPONSE), STD_TIMEOUT); 194 | 195 | // if not a good response, break out and be done 196 | if (response.response != RESP_GOOD) 197 | break; 198 | 199 | fseek(fp, (long) (header.lba * 512), SEEK_SET); 200 | fwrite(buffer, header.count, 512, fp); 201 | 202 | serial_recv(port_addr, &header, sizeof(struct S_HEADER), STD_TIMEOUT); 203 | } 204 | } 205 | } 206 | 207 | end_transfer: 208 | close_serial(port_addr); 209 | 210 | if (receiver) 211 | fclose(fp); 212 | free(buffer); 213 | 214 | return ret; 215 | } 216 | 217 | // parse the command line 218 | // usage: 219 | // /s = sender 220 | // /r = receiver 221 | // /x [0x,0] = starting lba (if sender) value can be in hex or integer (required for sender, ignored if receiver) 222 | // /l [0x,0] = count of sectors (if sender) value can be in hex or integer (required for sender, ignored if receiver) 223 | // /c [0x,0] = com port number (required for both sender and receiver) (1, 2, 3, 4) 224 | // /d [0x,0] = drive number 0x80, 0x81, etc. (required for sender, ignored if receiver) 225 | // /M = transfer maximum amount of data each transfer (optional for sender, ignored if receiver) 226 | // /m = transfer only one sector at a time (optional for sender, ignored if receiver) 227 | // 228 | // example: (sender) 229 | // mcopyata /s /x 0 /l 1024 /c 2 /d 0x80 /M 230 | // example: (receiver) 231 | // mcopyata /r /c 1 232 | bool parse_cmnd_line(int argc, char *argv[]) { 233 | int i = 1; 234 | 235 | while (i> 8) & 0xFF)); 40 | outpb(address + SER_LINE_CNTRL, (bit8u) config); // set the configuration for the port 41 | outpb(address + SER_INT_ID, SER_ID_FIFO_TRIG_14 | SER_ID_TX_CLR | SER_ID_RX_CLR | SER_ID_FIFO_ENB); 42 | outpb(address + SER_MODEM_CNTRL, SER_MODEM_CTLR_RTS | SER_MODEM_CTLR_DTR); 43 | 44 | return TRUE; 45 | } 46 | 47 | void close_serial(const bit32u address) { 48 | outpb(address + SER_MODEM_CNTRL, 0x00); // lower the RTS and DTR 49 | } 50 | 51 | // if timeout != 0, this will check for a byte 'timeout' times before it times-out 52 | int serial_send(const bit32u address, void *buffer, const int count, const int timeout) { 53 | int cnt = 0, time; 54 | bit8u byte; 55 | bit8u *p = (bit8u *) buffer; 56 | 57 | outpb(address + SER_MODEM_CNTRL, SER_MODEM_CTLR_RTS | SER_MODEM_CTLR_DTR); 58 | while (cnt < count) { 59 | // time = timeout; 60 | do { 61 | byte = inpb(address + SER_MODEM_STATUS); 62 | if ((byte & 0x80) == 0) { 63 | printf("Error on send: 0x%02X\n", byte); 64 | return -1; 65 | } 66 | // if we have a timeout count and we have expired, return 67 | // if (timeout && ((time -= 2) < 1)) // -2 because there are 2 inpb's in this loop 68 | // return -1; 69 | // wait for transmit buffer to be empty 70 | byte = inpb(address + SER_LINE_STATUS); 71 | } while ((byte & 0x20) == 0); 72 | outpb(address + SER_TRANSMIT, p[cnt]); 73 | cnt++; 74 | } 75 | 76 | return cnt; 77 | } 78 | 79 | void serial_clear_buffer(const bit32u address) { 80 | while (inpb(address + SER_LINE_STATUS) & 0x01) 81 | inpb(address + SER_RECEIVE); 82 | } 83 | 84 | // if timeout != 0, this will check for a byte 'timeout' times before it times-out 85 | int serial_recv(const bit32u address, void *buffer, const int count, const int timeout) { 86 | int cnt = 0, time; 87 | bit8u byte, *p = (bit8u *) buffer; 88 | 89 | memset(buffer, 0xFF, 4); // 'clear' out the first dword so we don't get a false positive on the magic member 90 | 91 | outpb(address + SER_MODEM_CNTRL, SER_MODEM_CTLR_RTS | SER_MODEM_CTLR_DTR); 92 | while (cnt < count) { 93 | // time = timeout; 94 | do { 95 | byte = inpb(address + SER_LINE_STATUS); 96 | if (byte & 0x8E) { 97 | printf("Error on receive: 0x%02X\n", byte); 98 | return -1; 99 | } 100 | // if we have a timeout count and we have expired, return 101 | // if (timeout && (--time == 0)) 102 | // return -1; 103 | } while ((byte & 0x01) == 0); 104 | p[cnt] = inpb(address + SER_RECEIVE); 105 | cnt++; 106 | } 107 | 108 | return cnt; 109 | } 110 | -------------------------------------------------------------------------------- /mserial.h: -------------------------------------------------------------------------------- 1 | 2 | /* Author: Benjamin David Lunt 3 | * Forever Young Software 4 | * Copyright (c) 1984-2018 5 | * 6 | * This code is donated to the Freeware communitee. You have the 7 | * right to use it for learning purposes only. You may not modify it 8 | * for redistribution for any other purpose unless you have written 9 | * permission from the author. 10 | * 11 | * You may modify and use it in your own projects as long as they are 12 | * for non-profit only and not distributed. Any project for profit that 13 | * uses this code must have written permission from the author. 14 | * 15 | * For more information: 16 | * http://www.fysnet.net/blog/2018/08/index.html#20aug2018 17 | * Contact: 18 | * fys [at] fysnet [dot] net 19 | * 20 | * Last update: 20 Aug 2018 21 | * 22 | * compile using SmallerC (https://github.com/alexfru/SmallerC/) 23 | * smlrcc @make.txt 24 | * 25 | */ 26 | 27 | #ifndef MSERIAL_H 28 | #define MSERIAL_H 29 | 30 | #define SER_TIMEOUT (20 * 18) // 20 seconds 31 | 32 | // serial stuff 33 | #define SER_DIVISOR_LSB 0x000 34 | #define SER_DIVISOR_MSB 0x001 35 | #define SER_RECEIVE 0x000 36 | #define SER_TRANSMIT 0x000 37 | #define SER_INT_ENABLE 0x001 38 | #define SER_INT_ID 0x002 39 | #define SER_LINE_CNTRL 0x003 40 | #define SER_FIFO 0x003 41 | #define SER_MODEM_CNTRL 0x004 42 | #define SER_LINE_STATUS 0x005 43 | #define SER_MODEM_STATUS 0x006 44 | #define SER_SCRATCH 0x007 45 | #define SER_SPECIAL 0x007 46 | 47 | #define SER_DIV_LATCH_ON 0x80 // used to turn reg 0,1 into divisor latch 48 | 49 | #define SER_BAUD_1200 96 // baud rate divisors 50 | #define SER_BAUD_2400 48 51 | #define SER_BAUD_4800 24 52 | #define SER_BAUD_9600 12 53 | #define SER_BAUD_14400 8 54 | #define SER_BAUD_19200 6 55 | #define SER_BAUD_28800 4 56 | #define SER_BAUD_38400 3 57 | #define SER_BAUD_57600 2 58 | #define SER_BAUD_115200 1 59 | 60 | #define SER_STOP_1 0 // 1 stop bit per character 61 | #define SER_STOP_2 4 // 2 stop bits per character 62 | 63 | #define SER_BITS_5 0 // send 5 bit characters 64 | #define SER_BITS_6 1 // send 6 bit characters 65 | #define SER_BITS_7 2 // send 7 bit characters 66 | #define SER_BITS_8 3 // send 8 bit characters 67 | 68 | #define SER_PARITY_NONE 0 // no parity 69 | #define SER_PARITY_ODD 8 // odd parity 70 | #define SER_PARITY_EVEN 24 // even parity 71 | 72 | #define SER_ID_FIFO_TRIG_1 (0<<6) 73 | #define SER_ID_FIFO_TRIG_4 (1<<6) 74 | #define SER_ID_FIFO_TRIG_8 (2<<6) 75 | #define SER_ID_FIFO_TRIG_14 (3<<6) 76 | #define SER_ID_TX_CLR (1<<2) 77 | #define SER_ID_RX_CLR (1<<1) 78 | #define SER_ID_FIFO_ENB (1<<0) 79 | 80 | #define SER_MODEM_CTLR_RTS 2 81 | #define SER_MODEM_CTLR_DTR 1 82 | 83 | // time-out counts 84 | #define NO_TIMEOUT 0 85 | #define STD_TIMEOUT 256 // depending on the speed of the ISA bus, this can be around (256 * 200ns) = 51uS between each byte received/sent 86 | #define MAX_TIMEOUT 5000000 // depending on the speed of the ISA bus, this can be around (5,000,000 * 200ns) = 1,000,000uS = 1 Sec between each byte received/sent 87 | 88 | void serial_clear_buffer(const bit32u address); 89 | bool init_serial(const bit32u address, const int baud, const int config); 90 | void close_serial(const bit32u address); 91 | int serial_send(const bit32u address, void *buffer, const int count, const int timeout); 92 | int serial_recv(const bit32u address, void *buffer, const int count, const int timeout); 93 | 94 | #endif // MSERIAL_H 95 | -------------------------------------------------------------------------------- /portio.c: -------------------------------------------------------------------------------- 1 | 2 | /* Author: Benjamin David Lunt 3 | * Forever Young Software 4 | * Copyright (c) 1984-2018 5 | * 6 | * This code is donated to the Freeware communitee. You have the 7 | * right to use it for learning purposes only. You may not modify it 8 | * for redistribution for any other purpose unless you have written 9 | * permission from the author. 10 | * 11 | * You may modify and use it in your own projects as long as they are 12 | * for non-profit only and not distributed. Any project for profit that 13 | * uses this code must have written permission from the author. 14 | * 15 | * For more information: 16 | * http://www.fysnet.net/blog/2018/08/index.html#20aug2018 17 | * Contact: 18 | * fys [at] fysnet [dot] net 19 | * 20 | * Last update: 20 Aug 2018 21 | * 22 | * compile using SmallerC (https://github.com/alexfru/SmallerC/) 23 | * smlrcc @make.txt 24 | * 25 | */ 26 | 27 | #include "ctype.h" 28 | 29 | void outpb(const bit16u address, const bit8u val) { 30 | asm ( 31 | " mov edx,[ebp+8] \n" 32 | " mov eax,[ebp+12] \n" 33 | " out dx,al \n" 34 | ); 35 | } 36 | 37 | /* 38 | void outpw(const bit16u address, const bit16u val) { 39 | asm ( 40 | " mov edx,[ebp+8] \n" 41 | " mov eax,[ebp+12] \n" 42 | " out dx,ax \n" 43 | ); 44 | } 45 | 46 | void outpd(const bit16u address, const bit32u val) { 47 | asm ( 48 | " mov edx,[ebp+8] \n" 49 | " mov eax,[ebp+12] \n" 50 | " out dx,eax \n" 51 | ); 52 | } 53 | */ 54 | 55 | bit8u inpb(const bit16u address) { 56 | asm ( 57 | " mov edx,[ebp+8] \n" 58 | " xor eax,eax \n" 59 | " in al,dx \n" 60 | ); 61 | } 62 | 63 | /* 64 | bit16u inpw(const bit16u address) { 65 | asm ( 66 | " mov edx,[ebp+8] \n" 67 | " xor eax,eax \n" 68 | " in ax,dx \n" 69 | ); 70 | } 71 | 72 | bit32u inpd(const bit16u address) { 73 | asm ( 74 | " mov edx,[ebp+8] \n" 75 | " in eax,dx \n" 76 | ); 77 | } 78 | */ 79 | -------------------------------------------------------------------------------- /portio.h: -------------------------------------------------------------------------------- 1 | 2 | /* Author: Benjamin David Lunt 3 | * Forever Young Software 4 | * Copyright (c) 1984-2018 5 | * 6 | * This code is donated to the Freeware communitee. You have the 7 | * right to use it for learning purposes only. You may not modify it 8 | * for redistribution for any other purpose unless you have written 9 | * permission from the author. 10 | * 11 | * You may modify and use it in your own projects as long as they are 12 | * for non-profit only and not distributed. Any project for profit that 13 | * uses this code must have written permission from the author. 14 | * 15 | * For more information: 16 | * http://www.fysnet.net/blog/2018/08/index.html#20aug2018 17 | * Contact: 18 | * fys [at] fysnet [dot] net 19 | * 20 | * Last update: 20 Aug 2018 21 | * 22 | * compile using SmallerC (https://github.com/alexfru/SmallerC/) 23 | * smlrcc @make.txt 24 | * 25 | */ 26 | 27 | #ifndef PORTIO_H 28 | #define PORTIO_H 29 | 30 | void outpb(const bit16u address, const bit8u val); 31 | //void outpw(const bit16u address, const bit16u val); 32 | //void outpd(const bit16u address, const bit32u val); 33 | bit8u inpb(const bit16u address); 34 | //bit16u inpw(const bit16u address); 35 | //bit32u inpd(const bit16u address); 36 | 37 | #endif // PORTIO_H 38 | -------------------------------------------------------------------------------- /sys.c: -------------------------------------------------------------------------------- 1 | 2 | /* Author: Benjamin David Lunt 3 | * Forever Young Software 4 | * Copyright (c) 1984-2018 5 | * 6 | * This code is donated to the Freeware communitee. You have the 7 | * right to use it for learning purposes only. You may not modify it 8 | * for redistribution for any other purpose unless you have written 9 | * permission from the author. 10 | * 11 | * You may modify and use it in your own projects as long as they are 12 | * for non-profit only and not distributed. Any project for profit that 13 | * uses this code must have written permission from the author. 14 | * 15 | * For more information: 16 | * http://www.fysnet.net/blog/2018/08/index.html#20aug2018 17 | * Contact: 18 | * fys [at] fysnet [dot] net 19 | * 20 | * Last update: 20 Aug 2018 21 | * 22 | * compile using SmallerC (https://github.com/alexfru/SmallerC/) 23 | * smlrcc @make.txt 24 | * 25 | */ 26 | 27 | #include "ctype.h" 28 | #include "sys.h" 29 | 30 | // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 31 | // Call an interrupt 32 | // Return TRUE = carry set 33 | bool intx(const int i, struct REGS *regs) { 34 | asm ( 35 | " push fs ; \n" 36 | " push ds ; \n" // save ds 37 | " push es ; \n" // save es 38 | 39 | // the self-modifying code in huge model is quite elabrate 40 | "section .relot \n" // move to the relocation section 41 | " dd rel1 \n" // insert a relocation item 42 | 43 | "section .text \n" // return to the code section 44 | " db 0x66, 0xB8 \n" // start of the mov eax,xxxxxxxx instruction 45 | "rel1: \n" // here is where the relocation happens 46 | " dd intx1 \n" // assembler places SSSSOOOO in this location 47 | " mov bx,ax \n" // the assembler uses SSSSOOOO (where SmallerC uses xxxSSSSO, see below) 48 | " shr eax,16 \n" // low 16-bits is offset 49 | " mov ds,ax \n" // high 16-bits is segment 50 | " mov eax,[bp+8] \n" // self modify code to interrupt number 51 | // ds:bx -> intx1 below 52 | " mov [bx+1],al \n" // passed parameter is a 'constant', not a pointer, so is a literal value 53 | 54 | // serialize the instruction stream 55 | " jmp .next_instruct ; \n" // self mod code needs serialization... 56 | ".next_instruct: ; \n" 57 | 58 | " mov esi,[bp+12] ; \n" // esi contains both segment and offset values (xxxSSSSO) 59 | " ror esi,4 ; \n" // OxxxSSSS 60 | " mov ds,si ; \n" // 61 | " mov fs,si ; \n" // 62 | " shr esi,28 ; \n" // esi = 4 bit offset 63 | " push ebp ; \n" // save ebp 64 | " push esi ; \n" // save the pointer to our regs data (offset only) 65 | " mov eax,[si+ 0] ; \n" 66 | " mov ebx,[si+ 4] ; \n" 67 | " mov ecx,[si+ 8] ; \n" 68 | " mov edx,[si+12] ; \n" 69 | " mov edi,[si+20] ; \n" 70 | " mov ebp,[si+24] ; \n" 71 | " mov es,[si+34] ; \n" 72 | " mov ds,[si+32] ; \n" 73 | " mov esi,[fs:si+16] ; \n" 74 | "intx1: ; \n" 75 | " int 0x00 ; zero will be replaced with value above \n" 76 | " xchg esi,[esp] ; swap the esi values (before and after values) \n" 77 | " mov [fs:si+ 0],eax ; \n" 78 | " mov [fs:si+ 4],ebx ; \n" 79 | " mov [fs:si+ 8],ecx ; \n" 80 | " mov [fs:si+12],edx ; \n" 81 | " pop eax ; save the new esi value\n" 82 | " mov [fs:si+16],eax ; \n" // esi 83 | " mov [fs:si+20],edi ; \n" 84 | " mov [fs:si+24],ebp ; \n" 85 | " mov [fs:si+32],ds ; \n" 86 | " mov [fs:si+34],es ; \n" 87 | " pushfd ; retrieve eflags \n" 88 | " pop eax ; into eax \n" 89 | " mov [fs:si+28],eax ; then into regs->eflags \n" 90 | " pop ebp ; \n" 91 | " pop es ; \n" 92 | " pop ds ; \n" 93 | " pop fs ; \n" 94 | ); 95 | 96 | return (bool) (regs->eflags & 1); 97 | } 98 | 99 | // See if a key has been pressed. 100 | // Only works on an Intel Legacy BIOS machine in real mode 101 | int kbhit(void) { 102 | int ret = 0; 103 | 104 | asm ( 105 | " mov ah,01 \n" 106 | " int 16h \n" 107 | " pushfd \n" 108 | " pop eax \n" 109 | " mov [bp-4],eax \n" 110 | ); 111 | 112 | // ZF clear if keystroke available 113 | return ((ret & 0x00000040) == 0); 114 | } 115 | 116 | // get the scancode of a key pressed 117 | // (if no key has been pressed, it waits for one) 118 | // Only works on an Intel Legacy BIOS machine in real mode 119 | int getch(void) { 120 | int ret = 0; 121 | 122 | asm ( 123 | " mov ah,00 \n" 124 | " int 16h \n" 125 | " mov [bp-4],eax \n" 126 | ); 127 | 128 | return ret; 129 | } 130 | -------------------------------------------------------------------------------- /sys.h: -------------------------------------------------------------------------------- 1 | 2 | /* Author: Benjamin David Lunt 3 | * Forever Young Software 4 | * Copyright (c) 1984-2018 5 | * 6 | * This code is donated to the Freeware communitee. You have the 7 | * right to use it for learning purposes only. You may not modify it 8 | * for redistribution for any other purpose unless you have written 9 | * permission from the author. 10 | * 11 | * You may modify and use it in your own projects as long as they are 12 | * for non-profit only and not distributed. Any project for profit that 13 | * uses this code must have written permission from the author. 14 | * 15 | * For more information: 16 | * http://www.fysnet.net/blog/2018/08/index.html#20aug2018 17 | * Contact: 18 | * fys [at] fysnet [dot] net 19 | * 20 | * Last update: 20 Aug 2018 21 | * 22 | * compile using SmallerC (https://github.com/alexfru/SmallerC/) 23 | * smlrcc @make.txt 24 | * 25 | */ 26 | 27 | #ifndef SYS_H 28 | #define SYS_H 29 | 30 | struct REGS { 31 | bit32u eax; // 0 32 | bit32u ebx; // 4 33 | bit32u ecx; // 8 34 | bit32u edx; // 12 35 | bit32u esi; // 16 36 | bit32u edi; // 20 37 | bit32u ebp; // 24 38 | bit32u eflags; // 28 39 | bit16u ds; // 32 40 | bit16u es; // 34 41 | }; 42 | 43 | bool intx(const int i, struct REGS *regs); 44 | 45 | int kbhit(void); 46 | int getch(void); 47 | 48 | 49 | #endif // SYS_H 50 | --------------------------------------------------------------------------------