├── .gitignore ├── Build ├── Debug │ └── .gitignore └── Release │ └── .gitignore ├── Inc ├── biosndos.h ├── dos.inc ├── handlers.h ├── tsrplex.h ├── tsrplex.inc └── tsrresic.h ├── README.md ├── Src ├── biosndos.asm ├── handlers.c ├── mainc.c ├── tsrplex.asm └── tsrresic.asm ├── makefile └── test └── send_message.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.err -------------------------------------------------------------------------------- /Build/Debug/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore -------------------------------------------------------------------------------- /Build/Release/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore -------------------------------------------------------------------------------- /Inc/biosndos.h: -------------------------------------------------------------------------------- 1 | #ifndef BIOSNDOS_H_ 2 | #define BIOSNDOS_H_ 3 | #pragma once 4 | 5 | #include 6 | 7 | extern uint16_t GetCursorCoordinates(void); 8 | extern void SetCursorCoordinates(uint16_t coordinates); 9 | extern void PrintCharacterWithTeletypeOutput(char character); 10 | 11 | #endif /* BIOSNDOS_H_ */ 12 | -------------------------------------------------------------------------------- /Inc/dos.inc: -------------------------------------------------------------------------------- 1 | %ifndef DOS_INC_ 2 | %define DOS_INC_ 3 | 4 | JUMP_ALIGN EQU 2 5 | WORD_ALIGN EQU 2 6 | 7 | ; DOS interrupts 8 | DOS_INTERRUPT_21h EQU 21h 9 | DOS_TSR_MULTIPLEX_INTERRUPT_2Fh EQU 2Fh 10 | 11 | ; DOS 21h functions 12 | SET_INTERRUPT_VECTOR EQU 25h 13 | GET_SYSTEM_TIME EQU 2Ch 14 | GET_INTERRUPT_VECTOR EQU 35h 15 | FREE_MEMORY EQU 49h 16 | GET_CURRENT_PSP_ADDRESS EQU 62h 17 | 18 | 19 | ; DOS Program Segment Prefix (PSP, first 256 (100h) bytes on top of program) 20 | struc PSP 21 | .int20hInstruction resb 2 22 | .wSizeOfMemoryInParagraphs resb 2 23 | .reservedAt4h resb 1 24 | .callToDosFunctionDispatcher resb 5 25 | .fpInt22hTerminate resb 4 26 | .fpInt23hCtrlC resb 4 27 | .fpInt24hCriticalError resb 4 28 | .reservedAt16h resb 22 29 | .wEnvironmentSegment resb 2 30 | .reservedAt2Eh resb 34 31 | .int21hAndRetfInstructions resb 3 32 | .reservedAt53h resb 9 33 | .FCB1 resb 16 34 | .FCB2 resb 20 35 | .DiskTransferArea: 36 | .bCommandLineLength resb 1 37 | .szCommandLine resb 127 38 | endstruc 39 | 40 | 41 | %endif ; DOS_INC_ 42 | -------------------------------------------------------------------------------- /Inc/handlers.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | Portions and patterns taken from mTCP: 4 | Copyright (C) 2005-2020 Michael B. Brutman (mbbrutman@gmail.com) 5 | mTCP web page: http://www.brutman.com/mTCP 6 | 7 | mTCP is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | mTCP is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with mTCP. If not, see . 19 | 20 | */ 21 | 22 | #ifndef HANDLERS_H_ 23 | #define HANDLERS_H_ 24 | #pragma once 25 | 26 | // Headers required by this file 27 | #include 28 | #include "tsrplex.h" 29 | 30 | #define NUMBER_OF_TSR_HOOKS 3 31 | #define TSR_IDENTIFICATION_STRING_LENGTH 13 32 | #define TSR_IDENTIFICATION_STRING "TsrPushNotify" 33 | #define SYSTEM_TIMER_TICK_INTERRUPT_1C 0x1C 34 | #define DOS_IDLE_INTERRUPT_28h 0x28 35 | #define DOS_TSR_MULTIPLEX_INTERRUPT_2F 0x2F 36 | 37 | #define TICKS_BEFORE_DRAWING_THE_PUSH_NOTIFICATION 18 // 18 * 55ms = 1 second 38 | #define COLUMNS_ON_SCREEN 80 39 | 40 | #define PACKET_BUFFER_LEN 1514 41 | #define PUSH_NOTIFICATION_BUFFER_SIZE 75 42 | 43 | #define UDP_PROTOCOL 17 44 | #define ETHERNET_HEADER_SIZE_BYTES 14 45 | 46 | typedef uint16_t EtherType; // 16 bits representing an Ethernet frame type 47 | typedef uint8_t EthAddr_t[6]; // An Ethernet address is 6 bytes 48 | 49 | typedef struct ResidentData 50 | { 51 | __segment pspSegment; 52 | volatile uint8_t tickCounter; 53 | uint8_t tsrID; 54 | char tsrIdString[TSR_IDENTIFICATION_STRING_LENGTH]; 55 | TSR_HOOK tsrHooks[NUMBER_OF_TSR_HOOKS]; 56 | char data[PUSH_NOTIFICATION_BUFFER_SIZE]; 57 | uint8_t Perform_Packet_Processing; 58 | uint8_t Buffer[ PACKET_BUFFER_LEN ]; 59 | uint16_t Buffer_len; 60 | const char *PKT_DRVR_EYE_CATCHER; 61 | uint8_t *Buffer_packetBeingCopied; 62 | uint16_t Packet_handle; // Provided by the packet driver 63 | uint8_t Packet_int; // Provided during initialization 64 | uint16_t udpDestPort; // Provided during initialization 65 | 66 | } RESIDENT_DATA; 67 | 68 | 69 | typedef struct IpHeader { 70 | 71 | uint8_t versHlen; // vers:4, Hlen:4 72 | uint8_t service_type; 73 | uint16_t total_length; 74 | 75 | // Fragmentation support 76 | // flags 0 to 15 77 | // 0: always 0 78 | // 1: 0=May Fragment, 1=Don't Fragment 79 | // 2: 0=Last Fragment, 1=More Fragments 80 | // 3 to 15: Fragment offset in units of 8 bytes 81 | 82 | uint16_t ident; 83 | uint16_t flags; // flags:3, frag_offset:13 84 | 85 | uint8_t ttl; 86 | uint8_t protocol; 87 | uint16_t chksum; 88 | 89 | uint8_t ip_src[4]; 90 | uint8_t ip_dest[4]; 91 | } IP_HEADER; 92 | 93 | 94 | typedef struct UdpHeader { 95 | // All of these need to be in network byte order. 96 | uint16_t src; 97 | uint16_t dst; 98 | uint16_t len; 99 | uint16_t chksum; 100 | } UDP_HEADER; 101 | 102 | 103 | enum TsrHooks 104 | { 105 | SYSTEM_TIMER_TICK_1Ch, 106 | DOS_IDLE_28h, 107 | DOS_TSR_MULTIPLEX_2Fh 108 | }; 109 | 110 | 111 | //********// 112 | //* Data *// 113 | //********// 114 | 115 | extern RESIDENT_DATA g_residentData; 116 | 117 | 118 | //*************// 119 | //* Functions *// 120 | //*************// 121 | 122 | extern void SetCursorToPushNotificationLocation(void); 123 | extern uint16_t GetSizeOfResidentSegmentInParagraphs(void); 124 | extern void Buffer_init( RESIDENT_DATA far* residentData); 125 | extern int8_t Packet_init( uint8_t packetInt, uint16_t udpDestPort, RESIDENT_DATA far* residentData ); 126 | extern int8_t Packet_release_type( uint16_t Packet_handle, uint8_t Packet_int ); 127 | extern void Buffer_startReceiving( RESIDENT_DATA far* residentData ); 128 | extern void Buffer_stopReceiving( RESIDENT_DATA far* residentData ); 129 | 130 | 131 | #endif /* HANDLERS_H_ */ 132 | -------------------------------------------------------------------------------- /Inc/tsrplex.h: -------------------------------------------------------------------------------- 1 | #ifndef TSRPLEX_H_ 2 | #define TSRPLEX_H_ 3 | #pragma once 4 | 5 | typedef void far __interrupt (*INTERRUPT_HANDLER)(void); 6 | typedef void (*INTERRUPT_HANDLER_OFFSET)(union INTPACK registers); 7 | 8 | typedef struct TsrHook 9 | { 10 | INTERRUPT_HANDLER previousHandler; 11 | INTERRUPT_HANDLER_OFFSET ourHandler; 12 | uint8_t interruptNumber; 13 | uint8_t flags; 14 | } TSR_HOOK; 15 | 16 | // Defines for TSR_HOOK.flags 17 | #define FLG_TSRHOOK_INSTALLED (1<<0) 18 | 19 | 20 | typedef enum MultiplexFunction 21 | { 22 | INSTALLATION_CHECK, 23 | GET_RESIDENT_DATA_AND_PREPARE_FOR_UNLOAD 24 | } MULTIPLEX_FUNCTION; 25 | 26 | 27 | typedef enum TsrError 28 | { 29 | NO_ERROR, 30 | ANOTHER_INSTANCE_OF_OUR_TSR_LOADED, 31 | NO_FREE_ID_AVAILABLE, 32 | TSR_HOOK_ALREADY_INSTALLED, 33 | TSR_HOOK_NOT_INSTALLED, 34 | TSR_HOOK_NO_LONGER_ON_TOP_OF_CHAIN, 35 | UNKNOWN_MULTIPLEX_FUNCTION, 36 | FAILED_TO_FREE_MEMORY, 37 | COULD_NOT_LOAD_PACKET 38 | } TSR_ERROR; 39 | 40 | 41 | typedef struct MultiplexSearch 42 | { 43 | char far* inFarIdString; 44 | 45 | TSR_ERROR outError; // Must be a byte (Open Watcom default for small enums) 46 | uint8_t outID; 47 | } MULTIPLEX_SEARCH; 48 | 49 | 50 | typedef struct MultiplexIO 51 | { 52 | union 53 | { 54 | void far* ioPtrParam; 55 | uint16_t ioWordParam; 56 | }; 57 | MULTIPLEX_FUNCTION inFunction; 58 | uint8_t inTsrID; 59 | union 60 | { 61 | TSR_ERROR outTsrError; 62 | uint16_t align; 63 | }; 64 | } MULTIPLEX_IO; 65 | 66 | 67 | //******************************// 68 | //* In-Line Assembly functions *// 69 | //******************************// 70 | 71 | extern uint16_t htons( uint16_t ); 72 | #pragma aux htons = \ 73 | "xchg al, ah" \ 74 | parm [ax] \ 75 | modify [ax] \ 76 | value [ax]; 77 | 78 | #define ntohs( x ) htons( x ) 79 | 80 | extern __segment GetCodeSegment(void); 81 | #pragma aux GetCodeSegment = \ 82 | "mov ax, cs" /* Copy CS to AX */ \ 83 | value [ax]; /* Return value in AX */ 84 | 85 | extern void LoadCodeSegmentToDataSegment(void); 86 | #pragma aux LoadCodeSegmentToDataSegment = \ 87 | "push cs" /* Copy CS... */ \ 88 | "pop ds" /* ...to DS */ \ 89 | modify []; /* No registers corrupted */ 90 | 91 | extern void EnableInterrupts(void); 92 | #pragma aux EnableInterrupts = \ 93 | "sti" \ 94 | modify []; 95 | 96 | extern void DisableInterrupts(void); 97 | #pragma aux DisableInterrupts = \ 98 | "cli" \ 99 | modify []; 100 | 101 | 102 | //*************// 103 | //* Functions *// 104 | //*************// 105 | 106 | extern __segment TsrPlex_GetCurrentPspSegment(void); 107 | extern void TsrPlex_FindMultiplexID(MULTIPLEX_SEARCH* pMultiplexSearch); 108 | extern TSR_ERROR TsrPlex_InstallTsrHooks(TSR_HOOK far tsrHooks[], uint16_t count); 109 | extern TSR_ERROR TsrPlex_UninstallTsrHooks(TSR_HOOK far tsrHooks[], uint16_t count); 110 | extern void TsrPlex_MultiplexCall(MULTIPLEX_IO* pMultiplexIO); 111 | extern TSR_ERROR TsrPlex_FreeEnvironmentalBlockFromPSP(__segment pspSegment); 112 | extern TSR_ERROR TsrPlex_FreePspToRemoveTsrFromMemory(__segment pspSegment); 113 | 114 | 115 | #endif /* TSRPLEX_H_ */ 116 | -------------------------------------------------------------------------------- /Inc/tsrplex.inc: -------------------------------------------------------------------------------- 1 | %ifndef TSRPLEX_INC_ 2 | %define TSRPLEX_INC_ 3 | 4 | OUR_TSR_IDENTIFICATION_STRING_LENGTH EQU 13 5 | FIRST_APPLICATION_TSR_MULTIPLEX_ID EQU 0C0h 6 | LAST_APPLICATION_TSR_MULTIPLEX_ID EQU 0FFh 7 | NUMBER_OF_VALID_MULTIPLEX_IDS EQU (LAST_APPLICATION_TSR_MULTIPLEX_ID - FIRST_APPLICATION_TSR_MULTIPLEX_ID + 1) 8 | 9 | ; TSR Multiplex functions (AL register). 10 | ; Function 0 is installation check on all programs 11 | struc TSR_MULTIPLEX 12 | .InstallationCheck resb 1 ; Must be function 0 13 | .GetResidentDataToESDIandPrepareForUnload resb 1 14 | endstruc 15 | 16 | ; Error codes from TSR related functions 17 | struc TSR_ERROR 18 | .NoError resb 1 ; 0 19 | .AnotherInstanceOfOurTsrFound resb 1 ; 1 20 | .NoFreeIDsAvailable resb 1 ; 2 21 | .TsrHookAlreadyInstalled resb 1 ; 3 22 | .TsrHookNotInstalled resb 1 ; 4 23 | .TsrHookIsNoLongerTopmost resb 1 ; 5 24 | .UnknownMultiplexFunction resb 1 ; 6 25 | .FailedToFreeMemory resb 1 ; 7 26 | endstruc 27 | 28 | 29 | struc TSR_HOOK 30 | .ffnPreviousHandler resb 4 31 | .fnOurHandler resb 2 32 | .bInterrupt resb 1 33 | .bFlags resb 1 34 | endstruc 35 | 36 | ; Flag defines for TSR_HOOK.bFlags 37 | FLG_TSRHOOK_INSTALLED EQU (1<<0) 38 | 39 | 40 | 41 | struc MULTIPLEX_SEARCH 42 | .fszTsrId resb 4 43 | 44 | .bTSR_error resb 1 45 | .bID resb 1 46 | endstruc 47 | 48 | 49 | struc MULTIPLEX_IO 50 | .wIoWordParam: 51 | .dwIoPtrParam resb 4 52 | .bInFunction resb 1 53 | .bInTsrID resb 1 54 | .bOutTsrError resb 1 55 | resb 1 56 | endstruc 57 | 58 | 59 | %endif ; TSRPLEX_INC_ 60 | -------------------------------------------------------------------------------- /Inc/tsrresic.h: -------------------------------------------------------------------------------- 1 | #ifndef TSRRESIC_H_ 2 | #define TSRRESIC_H_ 3 | #pragma once 4 | 5 | #include "tsrplex.h" 6 | 7 | 8 | //*************// 9 | //* Functions *// 10 | //*************// 11 | 12 | #pragma aux TsrResiC_JumpToInterruptHandler aborts; // Jump instead of call this function 13 | extern void TsrResiC_JumpToInterruptHandler(INTERRUPT_HANDLER interruptHandler); 14 | 15 | 16 | #endif /* TSRRESIC_H_ */ 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MS-DOS Push TSR 2 | 3 | * Features code excerpts from Michael Brutman's [mTCP](http://www.brutman.com/mTCP/) 4 | * Feature code excerpts from [this Open Watclock example](https://www.vcfed.org/forum/forum/technical-support/vintage-computer-programming/23586-tsr-programs-with-open-watcom?p=304280#post304280) 5 | 6 | ## Building 7 | 1. Download and install Open Watcom 1.9 8 | 2. Download and install NASM 9 | 3. From there, it's just `wmake` which will produce a binary called `tsrpush.exe` 10 | 11 | ## Running 12 | Simple! 13 | 1. Load a Packet Driver 14 | 2. Start up `tsrpush.exe` on your MS-DOS system by passing in the Packet Driver Interrupt Vector Number and a UDP port for listening, i.e. 15 | `tsrpush.exe 0x65 20000` (use Packet Driver at Interrupt 0x65 and listen on UDP Port 20000) 16 | 17 | ## Unloading 18 | Simple! 19 | 1. Just type `tsrpush.exe` 20 | 21 | ## Testing 22 | See the `send_message.py` script in the test directory. Change the broadcast address to that of your network and the message accordingly. Run it, and you will see a push message in MS-DOS. 23 | 24 | ## Known Issues 25 | 1. Will not work with all programs resident (uses pretty primitive screen control) 26 | 2. Does not appear to receive new packets after entering and leaving protected mode (i.e. Windows 3.11) 27 | 3. Does not support ARP, DNS, or TCP (only supports UDP and access via IP address) 28 | 4. Only supports receiving broadcast packets and does not verify the broadcast sender (should update configuration to properly use mTCP's parseEnv, do away with passing in the packet driver interupt vector, and assign an IP address and verify broadcast address) 29 | 5. Does not send a reply to acknowledge a UDP packet -------------------------------------------------------------------------------- /Src/biosndos.asm: -------------------------------------------------------------------------------- 1 | CPU 8086 ; Allow 8088/8086 instructions only 2 | BITS 16 ; Set 16 bit code generation 3 | 4 | %include "dos.inc" 5 | 6 | 7 | ; Segment containing code 8 | SEGMENT BEGTEXT CLASS=CODE PUBLIC 9 | 10 | GLOBAL GetCursorCoordinates_, SetCursorCoordinates_ 11 | GLOBAL PrintCharacterWithTeletypeOutput_ 12 | 13 | 14 | ;-------------------------------------------------------------------- 15 | ; GetCursorCoordinates_ 16 | ; Parameters: 17 | ; Nothing 18 | ; Returns: 19 | ; AL: Hardware cursor column (X-coordinate) 20 | ; AH: Hardware cursor row (Y-coordinate) 21 | ; Corrupts registers: 22 | ; Nothing 23 | ;-------------------------------------------------------------------- 24 | GetCursorCoordinates_: 25 | push cx 26 | push bx 27 | 28 | mov ah, 03h ; VIDEO - GET CURSOR POSITION AND SIZE 29 | xor bx, bx ; BH = Page number 30 | int 10h 31 | xchg ax, dx 32 | 33 | pop bx 34 | pop cx 35 | ret 36 | 37 | 38 | ;-------------------------------------------------------------------- 39 | ; SetCursorCoordinates_ 40 | ; Parameters: 41 | ; AX: Cursor coordinates (low byte = column, high byte = row) 42 | ; Returns: 43 | ; Nothing 44 | ; Corrupts registers: 45 | ; AX 46 | ;-------------------------------------------------------------------- 47 | SetCursorCoordinates_: 48 | push dx 49 | push bx 50 | 51 | xchg dx, ax ; DX = Cursor coordinates 52 | mov ah, 02h ; VIDEO - SET CURSOR POSITION 53 | xor bx, bx ; BH = Page number 54 | int 10h 55 | 56 | pop bx 57 | pop dx 58 | ret 59 | 60 | 61 | ;-------------------------------------------------------------------- 62 | ; PrintCharacterWithTeletypeOutput_ 63 | ; Parameters: 64 | ; AL: Character to print 65 | ; Returns: 66 | ; Nothing 67 | ; Corrupts registers: 68 | ; AX 69 | ;-------------------------------------------------------------------- 70 | PrintCharacterWithTeletypeOutput_: 71 | push bx 72 | 73 | mov ah, 0Eh ; VIDEO - TELETYPE OUTPUT 74 | xor bx, bx ; BH = Page number 75 | int 10h 76 | 77 | pop bx 78 | ret 79 | -------------------------------------------------------------------------------- /Src/handlers.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | Portions and patterns taken from mTCP: 4 | Copyright (C) 2005-2020 Michael B. Brutman (mbbrutman@gmail.com) 5 | mTCP web page: http://www.brutman.com/mTCP 6 | 7 | mTCP is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | mTCP is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with mTCP. If not, see . 19 | 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include "handlers.h" 26 | #include "tsrresic.h" 27 | #include "biosndos.h" 28 | 29 | // All data and code here goes to the resident BEGTEXT code segment 30 | #pragma data_seg("BEGTEXT", "CODE"); 31 | #pragma code_seg("BEGTEXT", "CODE"); 32 | 33 | // Function prototypes 34 | static void Buffer_free( const uint8_t *buffer); 35 | static void Packet_process_internal( void ); 36 | static void __interrupt SystemTimerTickAtVector1Ch(union INTPACK registers); 37 | 38 | static void __interrupt DosIdleAtVector28h(union INTPACK registers); 39 | static inline bool TimeToDrawPushNotification(void); 40 | static void PrintPushNotificationToScreen(char *data); 41 | 42 | static void __interrupt DosTsrMultiplexAtVector2Fh(union INTPACK registers); 43 | static inline bool OurHandlerWasCalled(uint8_t tsrID); 44 | 45 | 46 | //********// 47 | //* Data *// 48 | //********// 49 | 50 | RESIDENT_DATA g_residentData = 51 | { 52 | 0, // RESIDENT_DATA.pspSegment 53 | 0, // .tickCounter 54 | 0, // .tsrID 55 | TSR_IDENTIFICATION_STRING, // .tsrIdString[] 56 | { // .tsrHooks[] 57 | { 0, (INTERRUPT_HANDLER_OFFSET)SystemTimerTickAtVector1Ch, SYSTEM_TIMER_TICK_INTERRUPT_1C, 0 }, 58 | { 0, (INTERRUPT_HANDLER_OFFSET)DosIdleAtVector28h, DOS_IDLE_INTERRUPT_28h, 0 }, 59 | { 0, (INTERRUPT_HANDLER_OFFSET)DosTsrMultiplexAtVector2Fh, DOS_TSR_MULTIPLEX_INTERRUPT_2F, 0 } 60 | } 61 | }; 62 | 63 | // The magic receiver function. 64 | // 65 | // This is the function called by the packet driver when it receives a packet. 66 | // There are actually two calls; one call to get a buffer for the new packet 67 | // and one call to tell us when the packet has been copied into the buffer. 68 | // 69 | // Once the second call is made a new buffer is available to use for processing. 70 | 71 | static void __interrupt receiver( union INTPACK r ) { 72 | 73 | LoadCodeSegmentToDataSegment(); 74 | 75 | if ( r.w.ax == 0 ) { 76 | if ( (r.w.cx>PACKET_BUFFER_LEN) || (g_residentData.Perform_Packet_Processing == 0) ) { 77 | r.w.es = r.w.di = 0; 78 | } 79 | else { 80 | r.w.es = FP_SEG( g_residentData.Buffer); 81 | r.w.di = FP_OFF( g_residentData.Buffer); 82 | } 83 | } 84 | else { 85 | g_residentData.Buffer_len = r.w.cx; 86 | Packet_process_internal(); 87 | PrintPushNotificationToScreen(g_residentData.data); 88 | } 89 | 90 | // Custom epilog code. Some packet drivers can handle the normal 91 | // compiler generated epilog, but the Xircom PE3-10BT drivers definitely 92 | // can not. 93 | 94 | _asm { 95 | pop ax 96 | pop ax 97 | pop es 98 | pop ds 99 | pop di 100 | pop si 101 | pop bp 102 | pop bx 103 | pop bx 104 | pop dx 105 | pop cx 106 | pop ax 107 | retf 108 | }; 109 | } 110 | 111 | // Packet_process_internal 112 | // 113 | 114 | static void Packet_process_internal( void ) { 115 | 116 | uint8_t *packet; 117 | uint16_t packet_len; 118 | EtherType protocol; 119 | uint16_t etherType; 120 | EthAddr_t *fromEthAddr; 121 | 122 | packet = g_residentData.Buffer; 123 | packet_len = g_residentData.Buffer_len; 124 | 125 | 126 | // Packet routing. 127 | // 128 | // Bytes 13 and 14 (packet[12] and packet[13]) have the protocol type 129 | // in them. 130 | // 131 | // Ip: 0800 132 | // 133 | // Compare 16 bits at a time. Because we are on a little-endian machine 134 | // flip the bytes that we are comparing with when treating the values 135 | // as 16 bit words. (The registered EtherType word is already flipped 136 | // to be in network byte order.) 137 | 138 | protocol = ( ( uint16_t * ) packet )[6]; 139 | etherType = ntohs( ( ( uint16_t * ) packet )[6] ); 140 | fromEthAddr = ( EthAddr_t * )( &packet[6] ); 141 | 142 | // If this is an IP Packet 143 | if ( etherType == 0x0800 ) { 144 | UDP_HEADER *udpHeader; 145 | uint8_t ipHeaderLen; 146 | uint16_t udpDestPort; 147 | uint16_t udpLen; 148 | char* udpData; 149 | uint8_t i; 150 | uint8_t ending_pos = 0; 151 | IP_HEADER *ipHeader = ( IP_HEADER * )( packet + ETHERNET_HEADER_SIZE_BYTES ); 152 | 153 | if ( ipHeader->protocol == UDP_PROTOCOL ) { 154 | 155 | ipHeaderLen = ( ( ipHeader->versHlen & 0xF ) << 2 ); 156 | udpHeader = ( UDP_HEADER * ) ( packet + ETHERNET_HEADER_SIZE_BYTES + ipHeaderLen ); 157 | udpDestPort = ntohs( udpHeader->dst ); 158 | 159 | if ( udpDestPort == g_residentData.udpDestPort ) { 160 | udpLen = ntohs( udpHeader->len ); 161 | udpData = ( char* ) (packet + ETHERNET_HEADER_SIZE_BYTES + ipHeaderLen + sizeof( UDP_HEADER ) ); 162 | 163 | for ( i = 0; i < udpLen - sizeof( UDP_HEADER ); i++ ) { 164 | if ( i == PUSH_NOTIFICATION_BUFFER_SIZE ) { 165 | break; 166 | } 167 | g_residentData.data[i] = udpData[i]; 168 | ending_pos = i; 169 | } 170 | 171 | for ( i = ending_pos + 1; i < PUSH_NOTIFICATION_BUFFER_SIZE; i++ ) { 172 | g_residentData.data[i] = ' '; 173 | } 174 | } 175 | } 176 | } 177 | } 178 | 179 | int8_t Packet_release_type( uint16_t Packet_handle, uint8_t Packet_int ) { 180 | 181 | int8_t rc = -1; 182 | 183 | union REGS inregs, outregs; 184 | struct SREGS segregs; 185 | 186 | inregs.h.ah = 0x3; 187 | inregs.x.bx = Packet_handle; 188 | 189 | int86x( Packet_int, &inregs, &outregs, &segregs ); 190 | 191 | if ( !outregs.x.cflag ) { 192 | rc = 0; 193 | } 194 | 195 | return rc; 196 | } 197 | 198 | 199 | 200 | //***********************************// 201 | //* System Timer Tick Interrupt 1Ch *// 202 | //***********************************// 203 | 204 | static void __interrupt SystemTimerTickAtVector1Ch(union INTPACK registers) 205 | { 206 | LoadCodeSegmentToDataSegment(); 207 | g_residentData.tickCounter++; 208 | 209 | // Cannot use _chain_intr() because it is part of the libraries that 210 | // were removed from memory when we called _dos_keep(). 211 | // TsrResiC_JumpToInterruptHandler() must be at the end of ISR function! 212 | TsrResiC_JumpToInterruptHandler(g_residentData.tsrHooks[SYSTEM_TIMER_TICK_1Ch].previousHandler); 213 | } 214 | 215 | 216 | //**************************// 217 | //* DOS Idle Interrupt 28h *// 218 | //**************************// 219 | 220 | static void __interrupt DosIdleAtVector28h(union INTPACK registers) 221 | { 222 | LoadCodeSegmentToDataSegment(); 223 | if ( TimeToDrawPushNotification() ) 224 | PrintPushNotificationToScreen(g_residentData.data); 225 | 226 | TsrResiC_JumpToInterruptHandler(g_residentData.tsrHooks[DOS_IDLE_28h].previousHandler); 227 | } 228 | 229 | static inline bool TimeToDrawPushNotification(void) 230 | { 231 | return (g_residentData.tickCounter % TICKS_BEFORE_DRAWING_THE_PUSH_NOTIFICATION) == 0; 232 | } 233 | 234 | static void PrintPushNotificationToScreen(char *data) 235 | { 236 | uint16_t initialCursorLocation; 237 | uint8_t i = 0; 238 | initialCursorLocation = GetCursorCoordinates(); 239 | SetCursorToPushNotificationLocation(); 240 | 241 | for ( i = 0; i < PUSH_NOTIFICATION_BUFFER_SIZE; i++ ){ 242 | PrintCharacterWithTeletypeOutput( data[i] ); 243 | } 244 | SetCursorCoordinates(initialCursorLocation); 245 | } 246 | 247 | void SetCursorToPushNotificationLocation(void) 248 | { 249 | SetCursorCoordinates(COLUMNS_ON_SCREEN - PUSH_NOTIFICATION_BUFFER_SIZE - 1); 250 | } 251 | 252 | 253 | //***********************************// 254 | //* DOS TSR Multiplex Interrupt 2Fh *// 255 | //***********************************// 256 | 257 | static void __interrupt DosTsrMultiplexAtVector2Fh(union INTPACK registers) 258 | { 259 | LoadCodeSegmentToDataSegment(); 260 | if ( OurHandlerWasCalled(registers.h.ah) ) 261 | { 262 | switch ( (MULTIPLEX_FUNCTION) registers.h.al ) 263 | { 264 | case INSTALLATION_CHECK: 265 | registers.w.di = (uint16_t) g_residentData.tsrIdString; 266 | registers.w.es = GetCodeSegment(); 267 | registers.h.al = 0xFF; // TSR installed 268 | return; 269 | 270 | case GET_RESIDENT_DATA_AND_PREPARE_FOR_UNLOAD: 271 | registers.w.di = (uint16_t) &g_residentData; 272 | registers.w.es = GetCodeSegment(); 273 | registers.h.al = NO_ERROR; 274 | return; 275 | 276 | default: 277 | registers.h.al = UNKNOWN_MULTIPLEX_FUNCTION; 278 | return; 279 | } 280 | } 281 | else TsrResiC_JumpToInterruptHandler(g_residentData.tsrHooks[DOS_TSR_MULTIPLEX_2Fh].previousHandler); 282 | } 283 | 284 | static inline bool OurHandlerWasCalled(uint8_t tsrID) 285 | { 286 | return g_residentData.tsrID == tsrID; 287 | } 288 | 289 | 290 | void Buffer_startReceiving( RESIDENT_DATA far* residentData ) { residentData->Perform_Packet_Processing = 1; } 291 | 292 | void Buffer_stopReceiving( RESIDENT_DATA far* residentData ) { residentData->Perform_Packet_Processing = 0; } 293 | 294 | 295 | // Packet_init 296 | // 297 | // First make sure that there is a packet driver located where the caller 298 | // has specified. If so, try to register to receive all possible EtherTypes 299 | // from it. 300 | 301 | int8_t Packet_init( uint8_t packetInt, uint16_t udpDestPort, RESIDENT_DATA far* residentData ) { 302 | 303 | union REGS inregs, outregs; 304 | struct SREGS segregs; 305 | 306 | uint16_t far *intVector = ( uint16_t far * ) MK_FP( 0x0, packetInt * 4 ); 307 | 308 | uint16_t eyeCatcherOffset = *intVector; 309 | uint16_t eyeCatcherSegment = *( intVector+1 ); 310 | 311 | char far *eyeCatcher = ( char far * ) MK_FP( eyeCatcherSegment, eyeCatcherOffset ); 312 | 313 | eyeCatcher += 3; // Skip three bytes of executable code 314 | 315 | if ( _fmemcmp( residentData->PKT_DRVR_EYE_CATCHER, eyeCatcher, 8 ) != 0 ) { 316 | return -1; 317 | } 318 | 319 | inregs.h.ah = 0x2; // Function 2 (access_type) 320 | inregs.h.al = 0x1; // Interface class (ethernet) 321 | inregs.x.bx = 0xFFFF; // Interface type (card/mfg) 322 | inregs.h.dl = 0; // Interface number (assume 0) 323 | segregs.ds = FP_SEG( NULL ); // Match all EtherTypes 324 | inregs.x.si = FP_OFF( NULL ); 325 | inregs.x.cx = 0; 326 | segregs.es = FP_SEG( receiver ); // Receiver function 327 | inregs.x.di = FP_OFF( receiver ); 328 | 329 | int86x( packetInt, &inregs, &outregs, &segregs ); 330 | 331 | if ( outregs.x.cflag ) { 332 | return outregs.h.dh; 333 | } 334 | 335 | residentData->Packet_int = packetInt; 336 | residentData->Packet_handle = outregs.x.ax; 337 | residentData->udpDestPort = udpDestPort; 338 | 339 | return 0; 340 | 341 | } 342 | 343 | 344 | void Buffer_init(RESIDENT_DATA far* residentData) { 345 | 346 | // Initialize Perform_Packet_Processing to zero so that we don't start receiving 347 | // data before the other data structures are ready. This happens because 348 | // we have to initialize the packet driver to get our MAC address, but 349 | // we need the MAC address to initialize things like ARP. This allows 350 | // us to initialize the packet driver without fear of receiving a packet. 351 | // (Any packet we get in this state will be tossed.) 352 | 353 | residentData->Perform_Packet_Processing = 0; 354 | residentData->PKT_DRVR_EYE_CATCHER = "PKT DRVR"; 355 | residentData->Packet_int = 0x0; 356 | residentData->data[0] = 0; 357 | } 358 | 359 | 360 | //**************************// 361 | //* End of BEGTEXT segment *// 362 | //**************************// 363 | 364 | /** 365 | * This function must the the last one in the BEGTEXT segment! 366 | * 367 | * GetSizeOfResidentSegment() must locate after all other resident functions and global variables. 368 | * This function must be defined in the last source file if there are more than 369 | * one source file containing resident functions. 370 | */ 371 | uint16_t GetSizeOfResidentSegmentInParagraphs(void) 372 | { 373 | uint16_t sizeInBytes; 374 | 375 | sizeInBytes = (uint16_t) GetSizeOfResidentSegmentInParagraphs; 376 | sizeInBytes += 0x100; // Size of PSP (do not add when building .COM executable) 377 | sizeInBytes += 15; // Make sure nothing gets lost in partial paragraph 378 | 379 | return sizeInBytes >> 4; 380 | } 381 | -------------------------------------------------------------------------------- /Src/mainc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "handlers.h" 6 | #include "biosndos.h" 7 | 8 | static RESIDENT_DATA far* GetFarPointerToResidentData(void); 9 | static TSR_ERROR FindLoadedInstanceOrNewID(RESIDENT_DATA far* pResidentData); 10 | static inline bool PreviousInstanceFound(TSR_ERROR tsrError); 11 | static inline bool NoTsrErrors(TSR_ERROR tsrError); 12 | 13 | static TSR_ERROR InstallTsrToMemory(uint8_t packetInt, uint16_t udpDestPort, RESIDENT_DATA far* pResidentData); 14 | static void ReturnToDosWithResidentSegmentInRAM(void); 15 | 16 | static TSR_ERROR RemoveTsrFromMemory(uint16_t tsrID); 17 | static RESIDENT_DATA far* GetResidentDataFromLoadedInstance(MULTIPLEX_IO* pMultiplexIO); 18 | static void UnchainInterruptHandlers(MULTIPLEX_IO* pMultiplexIO, const RESIDENT_DATA far* pResidentDataInRam); 19 | static TSR_ERROR UnloadLoadedInstanceFromMemory(__segment pspSegment); 20 | static void RemovePushNotificationFromScreen(void); 21 | 22 | static void PrintTsrError(TSR_ERROR tsrError); 23 | static char* GetTsrErrorString(TSR_ERROR tsrError); 24 | 25 | 26 | #define ReturnIfTsrError(tsrError) \ 27 | if ( (tsrError) != NO_ERROR ) \ 28 | return (tsrError) 29 | 30 | 31 | int main(int argc, char* argv[]) { 32 | uint8_t packetInt; 33 | uint16_t udpDestPort; 34 | 35 | RESIDENT_DATA far* pResidentData; 36 | TSR_ERROR tsrError; 37 | 38 | pResidentData = GetFarPointerToResidentData(); 39 | tsrError = FindLoadedInstanceOrNewID(pResidentData); 40 | if ( PreviousInstanceFound(tsrError) ) { 41 | tsrError = RemoveTsrFromMemory(pResidentData->tsrID); 42 | if ( NoTsrErrors(tsrError) ) { 43 | RemovePushNotificationFromScreen(); 44 | puts("TSR Unloaded Successfully."); 45 | } 46 | } 47 | else if ( NoTsrErrors(tsrError) ) { 48 | if (argc != 3) { 49 | puts("Usage: tsrpush.exe "); 50 | puts("Example: tsrpush.exe 0x65 20000"); 51 | return -1; 52 | } 53 | sscanf( argv[1], "%x", &packetInt ); 54 | udpDestPort = atoi( argv[2] ); 55 | 56 | tsrError = InstallTsrToMemory(packetInt, udpDestPort, pResidentData); 57 | if ( NoTsrErrors(tsrError) ) { 58 | Buffer_startReceiving(pResidentData); 59 | ReturnToDosWithResidentSegmentInRAM(); 60 | } 61 | } 62 | PrintTsrError(tsrError); 63 | return tsrError; 64 | } 65 | 66 | 67 | static RESIDENT_DATA far* GetFarPointerToResidentData(void) { 68 | __segment codeSegment; 69 | 70 | codeSegment = GetCodeSegment(); 71 | return codeSegment:>&g_residentData; 72 | } 73 | 74 | static TSR_ERROR FindLoadedInstanceOrNewID(RESIDENT_DATA far* pResidentData) { 75 | MULTIPLEX_SEARCH multiplexSearch; 76 | 77 | multiplexSearch.inFarIdString = FP_SEG(pResidentData):>pResidentData->tsrIdString; 78 | TsrPlex_FindMultiplexID(&multiplexSearch); 79 | pResidentData->tsrID = multiplexSearch.outID; 80 | return multiplexSearch.outError; 81 | } 82 | 83 | static inline bool PreviousInstanceFound(TSR_ERROR tsrError) { 84 | return tsrError == ANOTHER_INSTANCE_OF_OUR_TSR_LOADED; 85 | } 86 | 87 | static inline bool NoTsrErrors(TSR_ERROR tsrError) { 88 | return tsrError == NO_ERROR; 89 | } 90 | 91 | static TSR_ERROR InstallTsrToMemory(uint8_t packetInt, uint16_t udpDestPort, RESIDENT_DATA far* pResidentData) { 92 | TSR_ERROR tsrError = NO_ERROR; 93 | int rc; 94 | pResidentData->pspSegment = TsrPlex_GetCurrentPspSegment(); 95 | 96 | Buffer_init(pResidentData); 97 | 98 | rc = Packet_init(packetInt, udpDestPort, pResidentData); 99 | if ( rc != 0 ) { 100 | tsrError = COULD_NOT_LOAD_PACKET; 101 | } 102 | ReturnIfTsrError(tsrError); 103 | 104 | tsrError = TsrPlex_InstallTsrHooks(FP_SEG(pResidentData):>pResidentData->tsrHooks, NUMBER_OF_TSR_HOOKS); 105 | ReturnIfTsrError(tsrError); 106 | 107 | tsrError = TsrPlex_FreeEnvironmentalBlockFromPSP(pResidentData->pspSegment); 108 | PrintTsrError(tsrError); 109 | 110 | return NO_ERROR; // Do not quit, just leave 256 byte environmental block in RAM 111 | } 112 | 113 | static void ReturnToDosWithResidentSegmentInRAM(void) { 114 | uint16_t paragraphsToKeepInRAM; 115 | paragraphsToKeepInRAM = GetSizeOfResidentSegmentInParagraphs(); 116 | printf("TSR loaded in RAM with %u bytes used.\n", paragraphsToKeepInRAM<<4); 117 | puts("Run this program again to remove TSR from memory."); 118 | flushall(); // Flush all streams before returning to DOS 119 | _dos_keep(0, paragraphsToKeepInRAM); 120 | } 121 | 122 | 123 | static TSR_ERROR RemoveTsrFromMemory(uint16_t tsrID) { 124 | RESIDENT_DATA far* pResidentDataInRam; 125 | MULTIPLEX_IO multiplexIO; 126 | 127 | multiplexIO.inTsrID = tsrID; 128 | pResidentDataInRam = GetResidentDataFromLoadedInstance(&multiplexIO); 129 | ReturnIfTsrError(multiplexIO.outTsrError); 130 | 131 | Buffer_stopReceiving( pResidentDataInRam ); 132 | Packet_release_type( pResidentDataInRam->Packet_handle, pResidentDataInRam->Packet_int ); 133 | 134 | UnchainInterruptHandlers(&multiplexIO, pResidentDataInRam); 135 | ReturnIfTsrError(multiplexIO.outTsrError); 136 | 137 | return UnloadLoadedInstanceFromMemory(pResidentDataInRam->pspSegment); 138 | } 139 | 140 | static RESIDENT_DATA far* GetResidentDataFromLoadedInstance(MULTIPLEX_IO* pMultiplexIO) { 141 | pMultiplexIO->inFunction = GET_RESIDENT_DATA_AND_PREPARE_FOR_UNLOAD; 142 | TsrPlex_MultiplexCall(pMultiplexIO); 143 | return (RESIDENT_DATA far*) pMultiplexIO->ioPtrParam; 144 | } 145 | 146 | static void UnchainInterruptHandlers(MULTIPLEX_IO* pMultiplexIO, const RESIDENT_DATA far* pResidentDataInRam) { 147 | pMultiplexIO->outTsrError = TsrPlex_UninstallTsrHooks( 148 | FP_SEG(pResidentDataInRam):>pResidentDataInRam->tsrHooks, NUMBER_OF_TSR_HOOKS); 149 | } 150 | 151 | static TSR_ERROR UnloadLoadedInstanceFromMemory(__segment pspSegment) { 152 | // Call TsrPlex_FreeEnvironmentalBlockFromPSP() here if it has not 153 | // been freed earlier. 154 | /**/ 155 | 156 | return TsrPlex_FreePspToRemoveTsrFromMemory(pspSegment); 157 | } 158 | 159 | static void RemovePushNotificationFromScreen(void) { 160 | uint16_t initialCursorLocation; 161 | uint16_t i; 162 | 163 | initialCursorLocation = GetCursorCoordinates(); 164 | SetCursorToPushNotificationLocation(); 165 | for (i=0; i filename.obj 97 | OBJ += $(SRC_C:.c=.obj) # filename.c => filename.obj 98 | OBJ += $(SRC_CXX:.cpp=.obj) # filename.cpp => filename.obj 99 | 100 | # List of NASM generated dependency files (*.d): 101 | ASMDEPS = $(SRC_ASM:.asm=.d) # filename.asm => filename.d 102 | 103 | 104 | # Add -d in front of every define declaration 105 | DEFS = -d$(DEFINES: = -d) 106 | 107 | 108 | # Add -i (for NASM) and -i= (for WATCOM) in front of all header directories 109 | NASM_INCLUDE = -i$(HEADERS: = -i) 110 | WATCOM_INCLUDE = -i=$(HEADERS: =;) 111 | 112 | # Create WLINK directives for library files and search paths 113 | LIBPATH = LIBPATH $(HEADERS: =;) 114 | !ifdef LIBRARIES 115 | LIBRARY = LIBRARY $(LIBRARIES: =,) 116 | !else 117 | LIBRARY = 118 | !endif 119 | 120 | # Path + target file to be build 121 | TARGET = $(BUILD_DIR)$(PROG).$(BINARY_TYPE) 122 | 123 | 124 | ######################### 125 | # Compilers and linkers # 126 | ######################### 127 | 128 | # C compiler 129 | CC = wcc.exe 130 | 131 | # C++ compiler 132 | CXX = wpp.exe 133 | 134 | # Disassembler 135 | DISASM = wdis.exe 136 | 137 | # Assembly compiler 138 | AS = nasm.exe 139 | 140 | # Linker 141 | LD = wlink.exe 142 | 143 | # Resource script compiler 144 | RC = wrc.exe 145 | 146 | # Use this command to erase files. 147 | RM = -del /Q 148 | 149 | 150 | ############################# 151 | # Compiler and linker flags # 152 | ############################# 153 | 154 | # Common C and C++ compiler flags 155 | C_AND_CXX_FLAGS = -0 # Generate 8086/8088 instructions only 156 | C_AND_CXX_FLAGS += -bt=dos # Generate program for MS-DOS 157 | C_AND_CXX_FLAGS += -m$(MEMORY_MODEL) 158 | C_AND_CXX_FLAGS += $(DEFS) # Preprocessor defines 159 | C_AND_CXX_FLAGS += $(WATCOM_INCLUDE) # Set header file directory paths 160 | C_AND_CXX_FLAGS += -q # Operate quietly 161 | !ifeq BUILD debug # Debug version flags 162 | C_AND_CXX_FLAGS += -d2 # Full symbolic debugging information 163 | C_AND_CXX_FLAGS += -hd # Generate DWARF debugging information 164 | !else # Release version flags 165 | C_AND_CXX_FLAGS += -s # Remove stack overflow checks 166 | C_AND_CXX_FLAGS += -oh # Enable expensive optimizations (longer compiles) 167 | C_AND_CXX_FLAGS += -os # Favor code size over execution time in optimizations 168 | #C_AND_CXX_FLAGS += -ot # Favor execution time over code size in optimizations 169 | !endif 170 | 171 | # C compiler flags 172 | CFLAGS = $(C_AND_CXX_FLAGS) 173 | 174 | # C++ compiler flags 175 | CXXFLAGS = $(C_AND_CXX_FLAGS) 176 | 177 | # Assembly compiler flags 178 | ASFLAGS = -f obj # Object format 179 | ASFLAGS += $(DEFS) # Preprocessor defines 180 | ASFLAGS += $(NASM_INCLUDE) # Set header file directory paths 181 | ASFLAGS += -Worphan-labels # Warn about labels without colon 182 | ASFLAGS += -O9 # Optimize operands to their shortest forms 183 | !ifeq BUILD debug # Debug version flags 184 | ASFLAGS += -g # Generate debug information 185 | !endif 186 | 187 | # Linker flags 188 | LDFLAGS = SYSTEM dos 189 | !ifeq BINARY_TYPE com 190 | LDFLAGS = SYSTEM com 191 | !endif 192 | LDFLAGS += OPTION QUIET # Operate quietly 193 | LDFLAGS += OPTION MAP=$(BUILD_DIR)$(PROG).map # Generate map file 194 | !ifeq BUILD debug # Debug version flags 195 | !endif 196 | 197 | 198 | ############################################ 199 | # Build process. Actual work is done here. # 200 | ############################################ 201 | 202 | # Make clean debug and release versions 203 | all: .SYMBOLIC 204 | @echo Building all... 205 | @$(MAKE) clean 206 | @$(MAKE) debug 207 | @$(MAKE) release 208 | @echo All build! 209 | 210 | # Clean 211 | clean: .SYMBOLIC 212 | @echo Cleaning files... 213 | @$(RM) Build\Debug\*.obj 214 | @$(RM) Build\Debug\*.d 215 | @$(RM) Build\Debug\*.lst 216 | @$(RM) Build\Debug\$(PROG).map 217 | @$(RM) Build\Debug\$(PROG).$(BINARY_TYPE) 218 | @$(RM) Build\Release\*.obj 219 | @$(RM) Build\Release\*.d 220 | @$(RM) Build\Release\*.lst 221 | @$(RM) Build\Release\$(PROG).map 222 | @$(RM) Build\Release\$(PROG).$(BINARY_TYPE) 223 | @echo Deleted *.d, *.lst, *.obj, $(PROG).map and $(PROG).$(BINARY_TYPE). 224 | 225 | # Build debug 226 | debug: .SYMBOLIC 227 | @echo Building debug... 228 | @$(MAKE) -z BUILD=debug internalBuild 229 | 230 | # Release build 231 | release: .SYMBOLIC 232 | @echo Building release... 233 | @$(MAKE) -z BUILD=release internalBuild 234 | 235 | # debug or release selected with command line macro 236 | internalBuild: $(TARGET) .PROCEDURE 237 | #internalBuild: debugData $(TARGET) .PROCEDURE 238 | @echo Build process completed. 239 | 240 | # Print debugging data (when modifying this makefile) 241 | debugData: .PROCEDURE 242 | @echo Start of makefile debug 243 | @echo MAKE: $(MAKE) 244 | @echo __MAKEOPTS__: $(__MAKEOPTS__) 245 | @echo BUILD: $(BUILD) 246 | @echo BUILD_DIR: $(BUILD_DIR) 247 | @echo OBJ: $(OBJ) 248 | @echo ASMDEPS: $(ASMDEPS) 249 | @echo DEFS: $(DEFS) 250 | @echo NASM_INCLUDE: $(NASM_INCLUDE) 251 | @echo WATCOM_INCLUDE: $(WATCOM_INCLUDE) 252 | @echo LIBRARY: $(LIBRARY) 253 | @echo LIBPATH: $(LIBPATH) 254 | @echo TARGET: $(TARGET) 255 | @echo End of makefile debug 256 | 257 | 258 | ####################################### 259 | # Actual object files are build below # 260 | ####################################### 261 | 262 | # Tell Make where destination .OBJ files are found 263 | .obj: $(BUILD_DIR) 264 | 265 | # linking rule remains the same as before. 266 | $(TARGET): $(OBJ) 267 | @echo Linking... 268 | @$(LD) $(LDFLAGS) NAME $(TARGET) PATH $(BUILD_DIR) FILE {$(OBJ) $(USER_OBJS)} $(LIBPATH) $(LIBRARY) 269 | @echo Successfully build $(BUILD) version to "$(BUILD_DIR)$^.". 270 | 271 | 272 | # .d files should be included here to get dependency checks working for .ASM files 273 | 274 | # Tell Make where source .ASM files are found 275 | .asm: $(SOURCES: =;) 276 | 277 | # Compile *.asm to *.obj and check for dependencies. 278 | .asm.obj: 279 | @$(AS) $(ASFLAGS) -o "$(BUILD_DIR)$^." -l "$(BUILD_DIR)$^&.lst" "$<" 280 | @$(AS) $(ASFLAGS) -o "$(BUILD_DIR)$^." -M "$<" > "$(BUILD_DIR)$^&.d" 281 | @echo Compiled "$<" to "$(BUILD_DIR)$^.". 282 | 283 | 284 | # Tell Make where source .C files are found 285 | .c: $(SOURCES: =;) 286 | 287 | # Compile *.c to *.obj and check for dependencies. 288 | .c.obj: .AUTODEPEND 289 | @$(CC) $(CFLAGS) -fo="$(BUILD_DIR)$^." "$<" 290 | @$(DISASM) -s -e -l="$(BUILD_DIR)$^&.lst" "$(BUILD_DIR)$^." 291 | @echo Compiled "$<" to "$(BUILD_DIR)$^.". 292 | 293 | 294 | # Tell Make where source .CPP files are found 295 | .cpp: $(SOURCES: =;) 296 | 297 | # Compile *.cpp to *.obj and check for dependencies. 298 | .cpp.obj: .AUTODEPEND 299 | @$(CXX) $(CXXFLAGS) -fo="$(BUILD_DIR)$^." "$<" 300 | @$(DISASM) -s -e -l="$(BUILD_DIR)$^&.lst" "$(BUILD_DIR)$^." 301 | @echo Compiled "$<" to "$(BUILD_DIR)$^.". 302 | -------------------------------------------------------------------------------- /test/send_message.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | UDP_IP = "192.168.1.255" 4 | UDP_PORT = 20000 5 | MESSAGE = "Hello, World! Push notifications in MS-DOS? You bet!!!" 6 | 7 | print("UDP target IP:", UDP_IP) 8 | print("UDP target port:", UDP_PORT) 9 | print("message:", MESSAGE) 10 | 11 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP 12 | sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) 13 | 14 | sock.sendto(bytes(MESSAGE, "utf-8"), (UDP_IP, UDP_PORT)) 15 | --------------------------------------------------------------------------------