;++ ; ; Quicky DLL to demonstrate how to add instructions to E11. ; ; By John Wilson . ; ; 02/23/2006 JMBW Created. ; ;-- .386 .model flat .code ; opc= 000077o ;our base opcode nopc= 1 ;# of opcodes starting at OPC ; ; All calls in and out of E11 use C __stdcall calling conventions: ; - arguments are pushed on stack, right to left ; - arguments are flushed by callee using "RET nnn" instruction ; - EBX/EBP/ESI/EDI must be preserved by callee ; - DF=0 on entry, must be 0 on return (i.e. string instructions go forward) ; - function return values are in EAX ; ; PC C compilers usually use stupid name decoration to catch really obvious ; bugs, very annoying. This are stripped out by the linker (so they don't ; appear in the .DLL file's symbol tables), but we have to use them to be able ; to match the same import library (E11.LIB) that works with C code. So, add a ; "_" prefix, and an "@nn" suffix, where nn is the number of bytes of arguments ; (most arguments are 4 bytes each in E11). ; extrn _GetOpcode@16:near,_RetOpcode@4:near extrn _GetReg@4:near,_SetReg@8:near ; DLL_PROCESS_ATTACH=1 DLL_PROCESS_DETACH=0 ; FALSE=0 TRUE=1 ;+ ; ; BOOL DllMain(HINSTANCE hInstDLL,DWORD fdwReason,LPVOID lpvReserved) ; ; Called at INSTALL and REMOVE (or QUIT). ; ; hInstDLL handle (just a unique number for this instance, not useful) ; fdwReason reason for calling: ; DLL_PROCESS_ATTACH => DLL is being installed ; DLL_PROCESS_DETACH => DLL is being removed ; lpvReserved on install: non-NULL means this DLL is the one in the ; INSTALL command line, so lpvReserved points at ; a DOS-style command line: ; .BYTE LENGTH ; .ASCII / command/ ; .BYTE 15 ; ; on remove: NULL means explicit REMOVE ; non-NULL means QUIT (but it's NOT a command ; line pointer in this case) ; ; Return value: ; TRUE (=1) for successful install, FALSE (=0) for failure. ; (Ignored on "remove" calls.) ; ;- DllMain: cmp dword ptr [esp+04h+04h],DLL_PROCESS_ATTACH ;are we newly alive? jne short dmain1 ;no, must be shutting down ; register our opcode(s) ; GetOpcode(dword first,dword count,void E11API (*exec)(),void E11API ; (*init)()) push offset opinit ;init = init routine (called on RESET etc.) push offset opexec ;exec = opcode execution routine push nopc ;count = # consecutive opcodes we want to catch push opc ;first = first opcode in block call _GetOpcode@16 ;request block test eax,eax ;success? jz short dmain2 ;no, return failure (EAX=FALSE) mov ds:ophnd,eax ;save handle for shutdown mov eax,TRUE ;we're happy jmp short dmain2 dmain1: ; unregister our opcode(s) ; RetOpcode(E11HANDLE handle) push dword ptr ds:ophnd ;handle = GetOpcode handle call _RetOpcode@4 ;return our block of opcodes dmain2: ret 3*4 ;return, flush three dwords of arguments ;+ ; ; opinit(void) ; ; Called on RESET instructions, or INIT/BOOT/etc. ; ;- opinit: ret ;nothing to do ;+ ; ; opexec(dword opcode) ; ; Called when one of the opcodes in our range is executed. ; ;- opexec: push ebp ;save registers push edi push esi push ebx mov ebx,[esp+4*4+04h] ;get opcode (above 4 regs and R.A.) ;(frame pointer regs are for dweebs!!!) sub ebx,opc ;subtract our base opcode call dword ptr ds:opcode[ebx*4] ;call opcode handler routine pop ebx ;restore pop esi pop edi pop ebp ret 4 ;return, flush one-dword argument ;+ ; ; UMUL ; ; 32x32 unsigned multiply ; ; We'll just use fixed registers, there aren't that many permutations anyway so ; it's not asking a lot of PDP-11 code to cooperate. ; ; R1:R0 = R3:R2 * R1:R0 ; PDP-11 flags are not changed ; ;- umul: push 3 ;GetReg(3) = get R3 call _GetReg@4 ;get it shrd ebx,eax,16d ;move low 16 bits of EAX into high 16 of EBX push 2 ;get R2 call _GetReg@4 ;(preserve EBX, among others) mov bx,ax ;insert AX in low 16 bits of EBX push 1 ;get R1 call _GetReg@4 shrd esi,eax,16d ;same deal as above push 0 ;get R0 call _GetReg@4 mov si,ax mov eax,esi ;load R1:R0 into EAX mul ebx ;unsigned multiply by R3:R2 in EBX ;(result in EDX:EAX) mov ebx,eax ;save out of the way of SetReg push eax ;value = low word of result (junk in LH) push 0 ;save in R0 call _SetReg@8 ;(preserve EBX) shr ebx,16d ;right-justify MSW push ebx ;value = high word of result push 1 ;save in R1 call _SetReg@8 ret ;that's it ; .data ;initialized data ; opcode label dword ;opcode handler list (# entries must = NOPC) dd umul ;000077 = UMUL ; .data? ;uninitialized data ; ophnd dd 1 dup(?) ;GetOpcode handle ; end DllMain