The Art of
ASSEMBLY LANGUAGE PROGRAMMING

Chapter Nineteen (Part 7)

Table of Content

Chapter Nineteen (Part 9)

CHAPTER NINETEEN:
PROCESSES, COROUTINES AND CONCURRENCY (Part 8)
19.3.2 - 32-bit Coroutines

19.3.2 32-bit Coroutines

The existing Standard Library (v1.0) coroutine package is not suitable for programs that use the 80386 and later 32 bit register sets. As mentioned earlier, the problem lies in the fact that the Standard Library only preserves the 16-bit registers when switching between processes. However, it is a relatively trivial extension to modify the Standard Library so that it saves 32 bit registers. To do so, just change the definition of the pcb (to make room for the 32 bit registers) and the sl_cocall routine:

                .386
                option  segment:use16

dseg            segment para public 'data'

wp              equ     <word ptr>

; 32-bit PCB.  Note we only keep the L.O. 16 bits of SP since we are
; operating in real mode.

pcb32           struc
regsp           word    ?
regss           word    ?
regip           word    ?
regcs           word    ?

regeax          dword   ?
regebx          dword   ?
regecx          dword   ?
regedx          dword   ?
regesi          dword   ?
regedi          dword   ?
regebp          dword   ?

regds           word    ?
reges           word    ?
regflags        dword   ?
pcb32           ends


DefaultPCB      pcb32   <>
DefaultCortn    pcb32   <>

CurCoroutine    dd      DefaultCortn    ;Points at the currently executing
                                        ; coroutine.
dseg            ends


cseg            segment para public 'slcode'

;============================================================================
;
; Coroutine support.
;
; COINIT- ES:DI contains the address of the current (default) process.

CoInit32        proc    far
                assume  ds:dseg
                push    ax
                push    ds
                mov     ax, dseg
                mov     ds, ax
                mov     wp dseg:CurCoroutine, di
                mov     wp dseg:CurCoroutine+2, es
                pop     ds
                pop     ax
                ret
CoInit32        endp


; COCALL32- transfers control to a coroutine.  ES:DI contains the address
; of the PCB.  This routine transfers control to that coroutine and then
; returns a pointer to the caller's PCB in ES:DI.

cocall32        proc    far
                assume  ds:dseg
                pushfd
                push    ds
                push    es                      ;Save these for later
                push    edi
                push    eax
                mov     ax, dseg
                mov     ds, ax
                cli                             ;Critical region ahead.


; Save the current process' state:

                les     di, dseg:CurCoroutine
                pop     es:[di].pcb32.regeax
                mov     es:[di].pcb32.regebx, ebx
                mov     es:[di].pcb32.regecx, ecx
                mov     es:[di].pcb32.regedx, edx
                mov     es:[di].pcb32.regesi, esi
                pop     es:[di].pcb32.regedi
                mov     es:[di].pcb32.regebp, ebp

                pop     es:[di].pcb32.reges
                pop     es:[di].pcb32.regds
                pop     es:[di].pcb32.regflags
                pop     es:[di].pcb32.regip
                pop     es:[di].pcb32.regcs
                mov     es:[di].pcb32.regsp, sp
                mov     es:[di].pcb32.regss, ss

                mov     bx, es                  ;Save so we can return in
                mov     ecx, edi                ; ES:DI later.
                mov     edx, es:[di].pcb32.regedi
                mov     es, es:[di].pcb32.reges
                mov     di, dx                  ;Point es:di at new PCB

                mov     wp dseg:CurCoroutine, di
                mov     wp dseg:CurCoroutine+2, es

                mov     es:[di].pcb32.regedi, ecx ;The ES:DI return values.
                mov     es:[di].pcb32.reges, bx

; Okay, switch to the new process:

                mov     ss, es:[di].pcb32.regss
                mov     sp, es:[di].pcb32.regsp
                mov     eax, es:[di].pcb32.regeax
                mov     ebx, es:[di].pcb32.regebx
                mov     ecx, es:[di].pcb32.regecx
                mov     edx, es:[di].pcb32.regedx
                mov     esi, es:[di].pcb32.regesi
                mov     ebp, es:[di].pcb32.regebp
                mov     ds, es:[di].pcb32.regds

                push    es:[di].pcb32.regflags
                push    es:[di].pcb32.regcs
                push    es:[di].pcb32.regip
                push    es:[di].pcb32.regedi
                mov     es, es:[di].pcb32.reges
                pop     edi
                iret
cocall32        endp


; CoCall32l works just like cocall above, except the address of the pcb
; follows the call in the code stream rather than being passed in ES:DI.
; Note: this code does *not* return the caller's PCB address in ES:DI.
;

cocall32l       proc    far
                assume  ds:dseg
                push    ebp
                mov     bp, sp
                pushfd
                push    ds
                push    es
                push    edi
                push    eax
                mov     ax, dseg
                mov     ds, ax
                cli                             ;Critical region ahead.

; Save the current process' state:

                les     di, dseg:CurCoroutine
                pop     es:[di].pcb32.regeax
                mov     es:[di].pcb32.regebx, ebx
                mov     es:[di].pcb32.regecx, ecx
                mov     es:[di].pcb32.regedx, edx
                mov     es:[di].pcb32.regesi, esi
                pop     es:[di].pcb32.regedi
                pop     es:[di].pcb32.reges
                pop     es:[di].pcb32.regds
                pop     es:[di].pcb32.regflags
                pop     es:[di].pcb32.regebp
                pop     es:[di].pcb32.regip
                pop     es:[di].pcb32.regcs
                mov     es:[di].pcb32.regsp, sp
                mov     es:[di].pcb32.regss, ss

                mov     dx, es:[di].pcb32.regip ;Get return address (ptr to
                mov     cx, es:[di].pcb32.regcs ; PCB address.
                add     es:[di].pcb32.regip, 4  ;Skip ptr on return.
                mov     es, cx                  ;Get the ptr to the new pcb
                mov     di, dx                  ; address, then fetch the
                les     di, es:[di]             ; pcb val.
                mov     wp dseg:CurCoroutine, di
                mov     wp dseg:CurCoroutine+2, es

; Okay, switch to the new process:

                mov     ss, es:[di].pcb32.regss
                mov     sp, es:[di].pcb32.regsp
                mov     eax, es:[di].pcb32.regeax
                mov     ebx, es:[di].pcb32.regebx
                mov     ecx, es:[di].pcb32.regecx
                mov     edx, es:[di].pcb32.regedx
                mov     esi, es:[di].pcb32.regesi
                mov     ebp, es:[di].pcb32.regebp
                mov     ds, es:[di].pcb32.regds

                push    es:[di].pcb32.regflags
                push    es:[di].pcb32.regcs
                push    es:[di].pcb32.regip
                push    es:[di].pcb32.regedi
                mov     es, es:[di].pcb32.reges
                pop     edi
                iret


cocall32l       endp
cseg            ends
                end

Chapter Nineteen (Part 7)

Table of Content

Chapter Nineteen (Part 9)

Chapter Nineteen: Processes, Coroutines and Concurrency (Part 8)
29 SEP 1996