]> 4ch.mooo.com Git - 16.git/blob - src/lib/doslib/hw/dos/dosasm.asm
added a bunch of things~ and midi stuff~
[16.git] / src / lib / doslib / hw / dos / dosasm.asm
1 ; dosasm.asm
2 ;
3 ; Assembly language support routines for dos.c
4 ; (C) 2011-2012 Jonathan Campbell.
5 ; Hackipedia DOS library.
6 ;
7 ; This code is licensed under the LGPL.
8 ; <insert LGPL legal text here>
9
10 extern _dpmi_entered ; BYTE
11 extern _dpmi_entry_point ; DWORD
12 extern _dpmi_private_data_segment ; word
13 extern _dpmi_rm_entry ; qword
14 extern _dpmi_pm_entry ; dword
15 extern _dpmi_pm_cs,_dpmi_pm_ds,_dpmi_pm_es,_dpmi_pm_ss
16
17 section .text class=CODE
18
19 ; NTS: If we code 'push ax' and 'popf' for the 16-bit tests in 32-bit protected mode we will screw up the stack pointer and crash
20 ;      so we avoid duplicate code by defining 'native' pushf/popf functions and 'result' to ax or eax depending on CPU mode
21 %if TARGET_MSDOS == 32
22  %define point_s esi
23  %define result eax
24  %define pushfn pushfd
25  %define popfn popfd
26 use32
27 %else
28  %define point_s si
29  %define result ax
30  %define pushfn pushf
31  %define popfn popf
32 use16
33 %endif
34
35 %if TARGET_MSDOS == 16
36  %ifndef MMODE
37   %error You must specify MMODE variable (memory model) for 16-bit real mode code
38  %endif
39 %endif
40
41 %if TARGET_MSDOS == 16
42  %ifidni MMODE,l
43   %define retnative retf
44   %define cdecl_param_offset 6  ; RETF addr + PUSH BP
45  %else
46   %ifidni MMODE,m
47    %define retnative retf
48    %define cdecl_param_offset 6 ; RETF addr + PUSH BP
49   %else
50    %define retnative ret
51    %define cdecl_param_offset 4 ; RET addr + PUSH BP
52   %endif
53  %endif
54 %else
55  %define retnative ret
56  %define cdecl_param_offset 8   ; RET addr + PUSH EBP
57 %endif
58
59 %ifndef TARGET_WINDOWS
60  %if TARGET_MSDOS == 16
61 ; cheap coding: put some variables here in the code segment. as real-mode
62 ; code there's nothing to stop us from leaving DS == CS on entry and letting
63 ; DPMI build an alias to our own code segment
64 l_dpmi_mode             db      0
65 l_dpmi_rm_entry         dd      0       ; also re-used to call entry!
66                         dw      0
67 l_dpmi_pm_entry         dd      0
68 ; we also need to record the segments given to us by the DPMI server so
69 ; that we can re-enter protected mode
70 l_dpmi_segs             dw      0,0,0,0
71 this_process_psp        dw      0
72
73 ; void __cdecl dpmi_enter_core(); /* Watcom's inline assembler is too limiting to carry out the DPMI entry and switch back */
74 global _dpmi_enter_core
75 _dpmi_enter_core:
76         ; 16-bit or 32-bit?
77         pushf
78         pusha
79         push            ds
80         push            es
81         push            cs
82         push            ss
83         cli
84         mov             ax,seg _dpmi_entered
85         mov             ds,ax
86         xor             ax,ax
87         mov             bl,byte [_dpmi_entered]
88         mov             byte [cs:l_dpmi_mode],bl ; the protected mode side of the function needs this
89         cmp             bl,32
90         jnz             .not32_ax
91         or              al,1            ; indicate 32-bit DPMI connection
92 .not32_ax:
93         ; so: AX=0 if 16-bit setup, AX=1 if 32-bit setup. Now for simplicity set DS==CS
94         mov             bx,seg _dpmi_private_data_segment       ; NTS may be zero if DPMI doesn't need it
95         mov             es,bx
96         mov             es,[es:_dpmi_private_data_segment]      ; NTS: ES = DPMI private data. Do not modify between here and call to DPMI entry
97
98         mov             bx,seg _dpmi_entry_point
99         mov             ds,bx
100         mov             bx,word [_dpmi_entry_point+0]
101         mov             word [cs:l_dpmi_rm_entry+0],bx
102         mov             bx,word [_dpmi_entry_point+2]
103         mov             word [cs:l_dpmi_rm_entry+2],bx
104
105         mov             bx,cs
106         mov             ds,bx
107         call far        word [cs:l_dpmi_rm_entry]
108         jnc             .entry_ok
109         ; ENTRY FAILED. Set entered flag to zero and return
110         mov             ax,seg _dpmi_entered
111         mov             ds,ax
112         mov             byte [_dpmi_entered],0
113         add             sp,4 ; discard saved CS+SS
114         pop             es
115         pop             ds
116         popa
117         popf
118         retnative
119 ; HERE: Entry succeeded. Get DPMI PM/RM entry points and then switch back to real mode.
120 ; note that because we entered with DS == CS the DPMI server should have CS != DS but both
121 ; refer to the same segment, as aliases. That makes our job simpler as we can use local storage
122 ; privately in the code segment.
123 .entry_ok:
124         mov             ax,0x0306
125         int             31h
126
127         ; BX:CX real to protected mode entry point
128         mov             word [l_dpmi_pm_entry+0],cx
129         mov             word [l_dpmi_pm_entry+2],bx
130
131         ; save the selectors preallocated by DPMI
132         mov             word [l_dpmi_segs+0],cs
133         mov             word [l_dpmi_segs+2],ds
134         mov             word [l_dpmi_segs+4],es
135         mov             word [l_dpmi_segs+6],ss
136
137         ; SI:DI (16-bit) or SI:EDI (32-bit) protected mode to real mode entry point
138         cmp             byte [l_dpmi_mode],32
139         jnz             .store_16
140         ; 32-bit storage, and return
141         mov             dword [l_dpmi_rm_entry+0],edi
142         mov             word [l_dpmi_rm_entry+4],si
143         pop             dx              ; restore SS into DX. DX will become SS
144         pop             ax              ; restore CS into AX. AX will become DS
145         mov             cx,ax           ; CX will become ES
146         mov             si,ax           ; SI will become CS
147         mov             bx,sp           ; BX will become SP
148         mov             di,.exit_ok     ; DI will become IP, so direct it at the exit point below
149         jmp far         dword [l_dpmi_rm_entry]
150 .store_16:
151         ; 16-bit storage, and return
152         mov             word [l_dpmi_rm_entry+0],di
153         mov             word [l_dpmi_rm_entry+2],si
154         pop             dx              ; restore SS into DX. DX will become SS
155         pop             ax              ; restore CS into AX. AX will become DS
156         mov             cx,ax           ; CX will become ES
157         mov             si,ax           ; SI will become CS
158         mov             bx,sp           ; BX will become SP
159         mov             di,.exit_ok     ; DI will become IP, so direct it at the exit point below
160         jmp far         word [l_dpmi_rm_entry]
161 ; jump back to realmode here
162 .exit_ok:
163
164 ; copy results to host variables
165         mov             ax,word [cs:l_dpmi_pm_entry]
166         mov             bx,word [cs:l_dpmi_pm_entry+2]
167         mov             cx,seg _dpmi_pm_entry
168         mov             ds,cx
169         mov             word [_dpmi_pm_entry+0],ax
170         mov             word [_dpmi_pm_entry+2],bx
171
172         mov             ax,word [cs:l_dpmi_rm_entry]
173         mov             bx,word [cs:l_dpmi_rm_entry+2]
174         mov             cx,word [cs:l_dpmi_rm_entry+4]
175         mov             dx,seg _dpmi_rm_entry
176         mov             ds,dx
177         mov             word [_dpmi_rm_entry+0],ax
178         mov             word [_dpmi_rm_entry+2],bx
179         mov             word [_dpmi_rm_entry+4],cx
180
181         mov             ax,word [cs:l_dpmi_segs+0]
182         mov             dx,seg _dpmi_pm_cs
183         mov             ds,dx
184         mov             word [_dpmi_pm_cs],ax
185
186         mov             ax,word [cs:l_dpmi_segs+2]
187         mov             dx,seg _dpmi_pm_ds
188         mov             ds,dx
189         mov             word [_dpmi_pm_ds],ax
190
191         mov             ax,word [cs:l_dpmi_segs+4]
192         mov             dx,seg _dpmi_pm_es
193         mov             ds,dx
194         mov             word [_dpmi_pm_es],ax
195
196         mov             ax,word [cs:l_dpmi_segs+6]
197         mov             dx,seg _dpmi_pm_ss
198         mov             ds,dx
199         mov             word [_dpmi_pm_ss],ax
200
201         ; now that DPMI is active, we have to hook real-mode INT 21h
202         ; to catch program termination, so we can forward that to the DPMI
203         ; server for proper DPMI cleanup
204         call            dpmi_hook_int21
205
206         pop             es
207         pop             ds
208         popa
209         popf
210         retnative
211  %endif
212 %endif
213
214 ; INT 21h hook:
215 ; We use DPMI entry and thunking back to real mode to let the host
216 ; program remain 16-bit. BUT: there's a problem. if the host program
217 ; exits normally with INT 21h via real mode, the DPMI server never gets
218 ; the message and it remains stuck running in the background. To make
219 ; DPMI exit normally, we have to hook INT 21h and reflect AH=0x4C to
220 ; protected mode.
221 %ifndef TARGET_WINDOWS
222  %if TARGET_MSDOS == 16
223 old_int21h              dd      0
224 dpmi_hook_int21:
225         push            es
226         push            ax
227         push            bx
228         push            cx
229         xor             ax,ax
230         mov             es,ax
231         mov             ax,cs
232         mov             bx,word [es:(0x21*4)]
233         mov             cx,word [es:(0x21*4)+2]
234         mov             word [es:(0x21*4)],dpmi_int21_hook_exit
235         mov             word [es:(0x21*4)+2],ax
236         mov             word [cs:old_int21h+0],bx
237         mov             word [cs:old_int21h+2],cx
238
239         ; also keep track of this process's PSP segment, so we can readily
240         ; identify WHO is calling INT 21h AH=0x4C and forward to DPMI only
241         ; for our process, not any other process.
242         mov             ah,0x62
243         int             21h
244         mov             word [cs:this_process_psp],bx
245
246         pop             cx
247         pop             bx
248         pop             ax
249         pop             es
250         ret
251
252 ; Our INT 21h hook. We're looking for any INT 21h AH=0x4C call coming from
253 ; this process. If the call comes from any other program in memory, the call
254 ; is forwarded without modification, so that DPMI does not prematurely exit
255 ; when subprocesses started by this program terminate.
256
257 ; This hack seems silly but apparently most DPMI servers do not monitor real-mode
258 ; INT 21h for the AH=0x4C call. If they never see the termination call from
259 ; protected mode, then they never clean up for this process and in most cases
260 ; (especially Windows) end up leaking selectors and other resources. So to avoid
261 ; memory leaks, we must forward INT 21h AH=0x4C to the protected mode side of
262 ; the DPMI server.
263 ;
264 ; TODO: This hook should also catch INT 21h AH=31 Terminate and Stay Resident,
265 ;       DPMI needs to keep those too!
266 ;
267 ; FIXME: How will this code catch cases where the calling program calls INT 21h
268 ;        from protected mode to terminate? Worst case scenario: DPMI cleans up
269 ;        and we never get a chance to remove our INT 21h hook.
270 dpmi_int21_hook_exit:
271         cmp             ah,0x4C
272         jz              .catch_exit
273         jmp far         word [cs:old_int21h]
274 .catch_exit:
275         ; this is our process terminating, not some subprocess, right?
276         ; we want to forward termination only for this process, not anyone else.
277         push            ax
278         push            bx
279         mov             ah,0x62         ; get PSP segment
280         int             21h
281         cmp             bx,word [cs:this_process_psp]
282         jz              .catch_exit_psp
283         pop             bx
284         pop             ax
285         jmp far         word [cs:old_int21h]
286 .catch_exit_psp:
287         pop             bx
288         pop             ax
289         ; restore the old vector
290         push            es
291         push            ax
292         push            bx
293         push            cx
294         xor             ax,ax
295         mov             es,ax
296         mov             ax,cs
297         mov             bx,word [cs:old_int21h+0]
298         mov             cx,word [cs:old_int21h+2]
299         mov             word [es:(0x21*4)],bx
300         mov             word [es:(0x21*4)+2],cx
301         pop             cx
302         pop             bx
303         pop             ax
304         pop             es
305         ; OK. Switch into protected mode.
306         ; use the segment values given to us by the DPMI server.
307         cli
308         mov             bp,ax                           ; save AX
309         mov             ax,word [cs:l_dpmi_segs+2]      ; AX becomes DS (so load DS from DPMI env)
310         mov             cx,ax                           ; CX becomes ES
311         mov             dx,ax                           ; DX becomes SS (doesn't matter)
312         mov             bx,sp                           ; BX becomes SP (doesn't matter)
313         mov             si,word [cs:l_dpmi_segs+0]      ; SI becomes CS (so load CS from DPMI env)
314         mov             di,.catch_exit_pmode            ; DI becomes IP
315         jmp far         word [cs:l_dpmi_pm_entry]
316 .catch_exit_pmode:
317         mov             ax,bp
318         mov             ah,0x4C
319         int             21h                     ; now issue INT 21h AH=0x4C where the DPMI server can see it
320         hlt
321  %endif
322 %endif
323
324 %if TARGET_MSDOS == 16
325  %ifndef TARGET_WINDOWS
326
327 ; WARNING: The caller must have ensured we are running on a 386 or higher, and that
328 ;          the DPMI entry points were obtained
329
330 ; __cdecl: right-to-left argument passing (meaning: caller does "push sz", "push lsrc", "push dst"...)
331 l_lin2fm_params:
332 l_lin2fm_param_dst:     dd      0       ; unsigned char far *dst
333 l_lin2fm_param_lsrc:    dd      0       ; uint32_t lsrc
334 l_lin2fm_param_sz:      dd      0       ; uint32_t sz
335                         ; = 12 bytes
336
337 l_rm_ret                dw      0
338 l_rm_reentry            dd      0
339                         dw      0
340
341 ; TODO: Export these so they are visible as C variables
342 ; we need these selectors for copy operation
343 l_lin2fm_src_sel        dw      0
344 l_lin2fm_dst_sel        dw      0
345
346 ; dpmi_pm_cs,dpmi_pm_ds,dpmi_pm_es,dpmi_pm_ss
347 ; int __cdecl dpmi_lin2fmemcpy_32(unsigned char far *dst,uint32_t lsrc,uint32_t sz);
348 global _dpmi_lin2fmemcpy_32
349 _dpmi_lin2fmemcpy_32:
350         push    bp
351         mov     bp,sp
352
353         ; copy params, we need them in protected mode
354         mov     eax,dword [bp+cdecl_param_offset+0]
355         mov     dword [cs:l_lin2fm_params+0],eax
356         mov     eax,dword [bp+cdecl_param_offset+4]
357         mov     dword [cs:l_lin2fm_params+4],eax
358         mov     eax,dword [bp+cdecl_param_offset+8]
359         mov     dword [cs:l_lin2fm_params+8],eax
360
361         pusha                   ; save all regs
362
363         push    ds
364         push    es
365
366         push    cs              ; realmode re-entry needs this
367         push    ss              ; realmode re-entry needs this
368         push    ds              ; realmode re-entry needs this
369
370         mov     ax,seg _dpmi_pm_entry
371         mov     ds,ax
372
373         xor     ax,ax
374         mov     word [cs:l_rm_ret],ax
375
376         mov     eax,dword [_dpmi_rm_entry+0]
377         mov     dword [cs:l_rm_reentry+0],eax
378
379         mov     ax,word [_dpmi_rm_entry+4]
380         mov     word [cs:l_rm_reentry+4],ax
381
382         mov     ax,word [_dpmi_pm_ds]
383         mov     cx,ax
384         mov     dx,word [_dpmi_pm_ss]
385         mov     bx,sp
386         mov     si,word [_dpmi_pm_cs]
387         mov     di,.entry_pm
388         call far word [_dpmi_pm_entry]
389         ; didn't make it. error return
390         add     sp,6            ; do not restore SS+CS+DS, just discard
391         pop     es
392         pop     ds
393         popa
394         pop     bp
395         xor     ax,ax           ; return 0 == no copy made
396         retnative
397 .entry_pm:
398
399         ; we need to allocate two selectors to do the copy operation with
400         cmp     word [l_lin2fm_src_sel],0
401         jnz     .sel_avail              ; if != 0, then skip code
402         ; allocate two descriptors
403         xor     ax,ax
404         mov     cx,2
405         int     31h
406         jnc     .sel_alloced            ; if carry clear, continue
407         jmp     .go_to_exit_pm          ; else return to RM with retval == 0
408 .sel_alloced:
409         ; we got two descriptors, store them
410         mov     word [l_lin2fm_src_sel],ax
411         add     ax,8                    ; obviously...
412         mov     word [l_lin2fm_dst_sel],ax
413         ; we need to make them data selectors
414         mov     ax,0x0009               ; DPMI Set Descriptor Access Rights
415         mov     bx,word [l_lin2fm_src_sel]
416         mov     cl,0xF0                 ; P=1 DPL=3 data expand-up r/o. I know DPMI says it must equal our level, but Windows always runs us Ring-3 so we can assume
417         xor     ch,ch                   ; 16-bit selector (we are 16-bit code!)
418         int     31h
419         jc      short $                 ; FIXME:For now, hang if the request failed
420         mov     ax,0x0008               ; DPMI Set Selector Limit
421         mov     bx,word [l_lin2fm_src_sel]
422         xor     cx,cx
423         xor     dx,dx
424         dec     dx                      ; CX:DX = 0000:FFFF
425         int     31h
426         ; and the other one
427         mov     ax,0x0009               ; DPMI Set Descriptor Access Rights
428         mov     bx,word [l_lin2fm_dst_sel]
429         mov     cl,0xF2                 ; P=1 DPL=3 data expand-up r/w. I know DPMI says it must equal our level, but Windows always runs us Ring-3 so we can assume
430         xor     ch,ch                   ; 16-bit selector (we are 16-bit code!)
431         int     31h
432         jc      short $                 ; FIXME:For now, hang if the request failed
433         mov     ax,0x0008               ; DPMI Set Selector Limit
434         mov     bx,word [l_lin2fm_dst_sel]
435         xor     cx,cx
436         xor     dx,dx
437         dec     dx                      ; CX:DX = 0000:FFFF
438         int     31h
439 .sel_avail:
440         ; OK, pull in source address (flat) from param and set the selector base
441         mov     ax,0x0007
442         mov     bx,word [l_lin2fm_src_sel]
443         mov     dx,word [l_lin2fm_param_lsrc+0] ; CX:DX = base
444         mov     cx,word [l_lin2fm_param_lsrc+2]
445         int     31h
446         ; and the dest address (realmode seg:off) too
447         movzx   eax,word [l_lin2fm_param_dst+2]
448         shl     eax,4
449         movzx   ebx,word [l_lin2fm_param_dst+0]
450         add     eax,ebx 
451         mov     dx,ax
452         shr     eax,16
453         mov     cx,ax
454         mov     bx,word [l_lin2fm_dst_sel]
455         mov     ax,0x0007
456         int     31h
457         ; alright then, do the memcpy
458         mov     cx,word [l_lin2fm_param_sz]
459         mov     word [l_rm_ret],cx              ; set return value too
460         push    ds
461         push    es
462         cld
463         mov     ax,word [l_lin2fm_src_sel]
464         mov     bx,word [l_lin2fm_dst_sel]
465         mov     ds,ax
466         mov     es,bx
467         xor     si,si
468         mov     di,si
469         rep     movsb                   ; ES:DI <- DS:SI
470         pop     es
471         pop     ds
472 .go_to_exit_pm:
473         ; NTS: when dpmi_enter_core() did it's job it made sure DS == CS
474         ; so the DPMI server would make DS an alias of CS in protected mode
475         pop     ax              ; AX = realmode DS
476         mov     cx,ax
477         pop     dx              ; DX = realmode SS
478         pop     si              ; SI = realmode CS
479         mov     bx,sp
480         mov     di,.exit_pm
481         call far dword [l_rm_reentry] ; NTS: We're using the 32-bit DPMI server, the RM entry point is 16:32 format
482 .exit_pm: ; NTS: Don't forget CS+DS+SS was pushed but the PM part popped them as part of returning
483         pop     es
484         pop     ds
485         popa
486
487         pop     bp
488         mov     ax,word [cs:l_rm_ret]
489         retnative
490
491 ; NOTE: This version of the code is written to work with 16-bit DPMI servers,
492 ;       and to work within the constraint that we could be run on a 286 where
493 ;       32-bit registers are not available.
494 ; dpmi_pm_cs,dpmi_pm_ds,dpmi_pm_es,dpmi_pm_ss
495 ; int __cdecl dpmi_lin2fmemcpy_16(unsigned char far *dst,uint32_t lsrc,uint32_t sz);
496 global _dpmi_lin2fmemcpy_16
497 _dpmi_lin2fmemcpy_16:
498         push    bp
499         mov     bp,sp
500
501         ; copy params, we need them in protected mode
502         mov     ax,word [bp+cdecl_param_offset+0]
503         mov     word [cs:l_lin2fm_params+0],ax
504         mov     ax,word [bp+cdecl_param_offset+2]
505         mov     word [cs:l_lin2fm_params+2],ax
506         mov     ax,word [bp+cdecl_param_offset+4]
507         mov     word [cs:l_lin2fm_params+4],ax
508         mov     ax,word [bp+cdecl_param_offset+6]
509         mov     word [cs:l_lin2fm_params+6],ax
510         mov     ax,word [bp+cdecl_param_offset+8]
511         mov     word [cs:l_lin2fm_params+8],ax
512         mov     ax,word [bp+cdecl_param_offset+10]
513         mov     word [cs:l_lin2fm_params+10],ax
514
515         pusha                   ; save all regs
516
517         push    ds
518         push    es
519
520         push    cs              ; realmode re-entry needs this
521         push    ss              ; realmode re-entry needs this
522         push    ds              ; realmode re-entry needs this
523
524         mov     ax,seg _dpmi_pm_entry
525         mov     ds,ax
526
527         xor     ax,ax
528         mov     word [cs:l_rm_ret],ax
529
530         mov     eax,dword [_dpmi_rm_entry+0]
531         mov     dword [cs:l_rm_reentry+0],eax
532
533         mov     ax,word [_dpmi_rm_entry+4]
534         mov     word [cs:l_rm_reentry+4],ax
535
536         mov     ax,word [_dpmi_pm_ds]
537         mov     cx,ax
538         mov     dx,word [_dpmi_pm_ss]
539         mov     bx,sp
540         mov     si,word [_dpmi_pm_cs]
541         mov     di,.entry_pm
542         call far word [_dpmi_pm_entry]
543         ; didn't make it. error return
544         add     sp,6            ; do not restore SS+CS+DS, just discard
545         pop     es
546         pop     ds
547         popa
548         pop     bp
549         xor     ax,ax           ; return 0 == no copy made
550         retnative
551 .entry_pm:
552
553         ; we need to allocate two selectors to do the copy operation with
554         cmp     word [l_lin2fm_src_sel],0
555         jnz     .sel_avail              ; if != 0, then skip code
556         ; allocate two descriptors
557         xor     ax,ax
558         mov     cx,2
559         int     31h
560         jnc     .sel_alloced            ; if carry clear, continue
561         jmp     .go_to_exit_pm          ; else return to RM with retval == 0
562 .sel_alloced:
563         ; we got two descriptors, store them
564         mov     word [l_lin2fm_src_sel],ax
565         add     ax,8                    ; obviously...
566         mov     word [l_lin2fm_dst_sel],ax
567         ; we need to make them data selectors
568         mov     ax,0x0009               ; DPMI Set Descriptor Access Rights
569         mov     bx,word [l_lin2fm_src_sel]
570         mov     cl,0xF0                 ; P=1 DPL=3 data expand-up r/o. I know DPMI says it must equal our level, but Windows always runs us Ring-3 so we can assume
571         xor     ch,ch                   ; 16-bit selector (we are 16-bit code!)
572         int     31h
573         jc      short $                 ; FIXME:For now, hang if the request failed
574         mov     ax,0x0008               ; DPMI Set Selector Limit
575         mov     bx,word [l_lin2fm_src_sel]
576         xor     cx,cx
577         xor     dx,dx
578         dec     dx                      ; CX:DX = 0000:FFFF
579         int     31h
580         ; and the other one
581         mov     ax,0x0009               ; DPMI Set Descriptor Access Rights
582         mov     bx,word [l_lin2fm_dst_sel]
583         mov     cl,0xF2                 ; P=1 DPL=3 data expand-up r/w. I know DPMI says it must equal our level, but Windows always runs us Ring-3 so we can assume
584         xor     ch,ch                   ; 16-bit selector (we are 16-bit code!)
585         int     31h
586         jc      short $                 ; FIXME:For now, hang if the request failed
587         mov     ax,0x0008               ; DPMI Set Selector Limit
588         mov     bx,word [l_lin2fm_dst_sel]
589         xor     cx,cx
590         xor     dx,dx
591         dec     dx                      ; CX:DX = 0000:FFFF
592         int     31h
593 .sel_avail:
594         ; OK, pull in source address (flat) from param and set the selector base
595         mov     ax,0x0007
596         mov     bx,word [l_lin2fm_src_sel]
597         mov     dx,word [l_lin2fm_param_lsrc+0] ; CX:DX = base
598         mov     cx,word [l_lin2fm_param_lsrc+2]
599         int     31h
600         ; and the dest address (realmode seg:off) too
601         mov     dx,word [l_lin2fm_param_dst+2]  ; DX = (seg << 4) + offset, CX = (seg >> 12) + (carry flag result of computing DX)
602         mov     cx,dx
603         shr     cx,12
604         shl     dx,4
605         add     dx,word [l_lin2fm_param_dst+0]
606         adc     cx,0                            ; CX:DX = 32-bit linear address
607         mov     bx,word [l_lin2fm_dst_sel]
608         mov     ax,0x0007
609         int     31h
610         ; alright then, do the memcpy
611         mov     cx,word [l_lin2fm_param_sz]
612         mov     word [l_rm_ret],cx              ; set return value too
613         push    ds
614         push    es
615         cld
616         mov     ax,word [l_lin2fm_src_sel]
617         mov     bx,word [l_lin2fm_dst_sel]
618         mov     ds,ax
619         mov     es,bx
620         xor     si,si
621         mov     di,si
622         rep     movsb                   ; ES:DI <- DS:SI
623         pop     es
624         pop     ds
625 .go_to_exit_pm:
626         ; NTS: when dpmi_enter_core() did it's job it made sure DS == CS
627         ; so the DPMI server would make DS an alias of CS in protected mode
628         pop     ax              ; AX = realmode DS
629         mov     cx,ax
630         pop     dx              ; DX = realmode SS
631         pop     si              ; SI = realmode CS
632         mov     bx,sp
633         mov     di,.exit_pm
634         call far word [l_rm_reentry] ; NTS: We're using the 16-bit DPMI server, the RM entry point is 16:16 format
635 .exit_pm: ; NTS: Don't forget CS+DS+SS was pushed but the PM part popped them as part of returning
636         pop     es
637         pop     ds
638         popa
639
640         pop     bp
641         mov     ax,word [cs:l_rm_ret]
642         retnative
643
644  %endif
645 %endif