; Listing11-3.asm ; ; A program that demonstrates classes ; for creating generators (no fibers). option casemap:none .nolist include aoalib.inc include aoaProcs.inc include aoaTypes.inc include aoaMacros.inc include aoaExcepts.inc include aoaClasses.inc includelib aoalib.lib .list ; Program title: .const align word ttlStr byte "Listing 11-3", 0 ; Class names: range_tName byte "range_t", 0 ; Define the generator_t class. ; This is an abstract base class from which ; other generator classes will be defined. class generator_t ; className- pointer to zString containing class name. className qword ? ; onHeap- True if generator object was allocated on the heap. onHeap byte ? ; Methods: ; All methods except create expect RSI to contain the THIS ; pointer and RDI to contain the address of the VMT. ;static create ; start parameters will vary by derived class. virtual start ; Generate returns the generator result in: ; 128-bit: RDX:RAX ; 64-bit: RAX ; 32-bit: EAX ; 16-bit: AX ; 8-bit: AL ; ; HO accumulator bits are undefined. Generator will ; not modify RDX if the generator returns 64 bits or ; less. virtual generate ; hasData will return true/false (1/0) in AL virtual hasData ; Delete returns nothing. virtual delete endc generator_t ; Still in .const section, emit the VMT: emitVMT generator_t ; The "this" pointer for generator_t: generator_t@ textequ <(generator_t ptr [rsi])> ;-------- ; ; Simple demonstration of a derived generator class: range_t ; ; Passed a pair of integers (startingVal and endingVal). ; Generates the starting integers from startingVal to ; endingVal. class range_t, inherits( generator_t ) ; Private data: startingVal dword ? ;Store generator parameters endingVal dword ? ; in these two locations override start, range_t$start override generate, range_t$generate override hasData, range_t$hasData override delete, range_t$delete endc range_t ; Still in .const section, emit the VMT: emitVMT range_t ; The "this" pointer for generator_t: range_t@ textequ <(range_t ptr [rsi])> ;------------------------------------------------------------- .data rangePtr qword ? range2Ptr qword ? ;------------------------------------------------------------- ; Class methods and procedures .code ;------ ; ; Constructor and methods for the abstract generator_t class: ; ; Just raise an exception if anyone tries to call any of ; these procedures or methods. generator_t$create proc int 3 generator_t$create endp generator_t$start textequ generator_t$hasData textequ generator_t$delete textequ generator_t$generate textequ ;********************************************************** ; ; Constructor for the range_t class. ; ; On entry, RSI contains the address of the range_t ; object to initialize, or NULL if this function ; is supposed to allocate storage for a new object. ; ; On return, RSI points at the object and RDI ; points at the VMT. This construction returns ; NULL in RSI if there was an error allocating or ; initializing the object. ; locals maps rsiSave, and rdiSave to the same offsets ; that xproc uses to preserve their values. Need to so this ; so we can change the return values of RSI and RDI later. locals range_t$create, \ rsiSave:qword, \ rdiSave:qword range_t$create xproc (frame, ) ; Note: this function makes Windows API calls. Would really ; like to preserve all registers except RSI and RDI across ; this call, so we must save the volatile registers ourselves. ; ; Note that volatile registers don't get restored if an ; exception occurs. local raxSave:qword, \ rcxSave:qword, \ thisPtr:qword, \ shadowStorage[64]:byte ; Preserve the volatile registers: mov raxSave, rax mov rcxSave, rcx ; Save THIS so we can check for NULL later. mov thisPtr, rsi mov ecx, sizeof range_t lea rdi, vmtAdrs(range_t) call generic$Class$Constructor mov rcx, thisPtr ;Original RSI value test rsi, rsi ;Did allocation fail? jz failure ; Check to see if object was allocated on heap. Set the ; onHeap field to true if it was: test rcx, rcx jnz dontSetHeap mov range_t@.onHeap, 1 ; Initialize pointer to field name: dontSetHeap: lea rcx, range_tName mov range_t@.className, rcx ; endxp is going to restore RSI and RDI as per the ; Windows' ABI. However, we really need to return ; THIS and VMT in them. Overwrite data on the stack: mov range_t$create$rsiSave, rsi mov range_t$create$rdiSave, rdi ; Need to return THIS in RAX: failure: mov rax, raxSave mov rcx, rcxSave range_t$create endxp() ; The following class methods for range_t override ; the abstract methods from the generator_t class. ; Note that range_t is an exceeding simple generator ; and really shouldn't be written using fibers; there ; is many times more work involved in invoking the ; fiber as there would be in just doing the range_t ; actions directly in the following methods. Because ; range_t is so trivial, however, it makes and easy- ; to-understand generator. So the following methods ; invoke the fiber procedure to demonstrate how ; it's done. ; start( startingValue, endingValue ); ; ; (Re)Sets the object's starting and ending values. range_t$start proc mov range_t@.startingVal, ecx mov range_t@.endingVal, edx ret range_t$start endp ; generate- If a value is available, returns the next generated ; value in EAX; if no data is available, it returns ; 0 in EAX. ; ; If data was avaiable, this function returns with ; the carry flag clear. ; ; If data was not available, this function returns ; with the carry flag set. range_t$generate proc mov eax, range_t@.startingVal cmp eax, range_t@.endingVal ja genError inc range_t@.startingVal clc ;Return no error ret genError: xor rax, rax stc ;Return error ret range_t$generate endp ; hasData returns (in AL) true/false depending on ; whether the generator can generate additional ; data. range_t$hasData proc mov eax, range_t@.startingVal cmp eax, range_t@.endingVal setbe al movzx eax, al ret range_t$hasData endp ; range_t$delete: ; ; The delete method shuts down the generator and ; deletes the fiber. range_t$delete proc ; If object was allocated on the heap, delete it here: cmp range_t@.onHeap, 0 je noFree mov rax, rsi ;Free object storage call mem$free noFree: ret range_t$delete endp ;-------------------------------------------------- ; ; Here is the main assembly language function. public asmMain asmMain xproc (frame, ) local shadowStorage[80]:byte ; Create a pair of range generator objects xor rsi, rsi ;Allocate on heap call range_t$create mov rangePtr, rsi ;Return value in RSI! xor rsi, rsi ;Allocate on heap call range_t$create mov range2Ptr, rsi ; Start the outer generator with the values 1..2: mov ecx, 1 mov edx, 2 inv range_t, start, range2Ptr ; Invoke the generators until they fail: outerLoop: inv range_t, hasData, range2Ptr cmp al, 0 je noMoreData inv range_t, generate, range2Ptr mov r15d, eax ;Save for later ; Start the inner generator with the values 1..5: mov ecx, 1 mov edx, 5 inv range_t, start, rangePtr innerLoop: inv range_t, hasData, rangePtr cmp al, 0 je outerLoop inv range_t, generate, rangePtr mov edx, eax mov r8d, r15d call print byte "Generated: inner=%d, outer=%d", nl, 0 jmp innerLoop noMoreData: call print byte "Generation done, cleaning up", nl, 0 ; Clean up after ourselves: inv range_t, delete, range2Ptr inv range_t, delete, rangePtr asmMain endxp() end