;
; PXObject
; 

%define PEXL_RTL_PXOBJECT_ASM
%include "pexl_rtl.h.asm"

; Notes for x86 implementation:

; Every pxobject has a 12-byte header, stored immediately preceding the returned
;   pointer.
;
; Refcounted GC means we can get automatic and deterministic destructor calls
;   (like Python, unlike D or Java).



	
; public void* pxo_new(size_t len)
;  Allocate a new PXObject
; @param  len   Number of octets to allocate
; @return void* Memory buffer (there's a pxobject header hidden 8 octets before)
DECLARE_MANGLED pxo_new, 4
	;
	push ebp
	mov ebp, esp
	push ebx	
	
	; eax := malloc( arg1 + PXO_SIZE )
	mov ebx, dword[ebp+8]
	add ebx, PXO_SIZE ; real alloced size
	push ebx
	call pxl_rtl_malloc
	
	; add default pxobj references
	mov dword[eax + PXO_OFFSET_REFCT], 1 ; refcount
	sub ebx, PXO_SIZE ; fake alloced size
	mov dword[eax + PXO_OFFSET_ALLOCSIZE], ebx ; buffsize
	mov dword[eax + PXO_OFFSET_DESTRUCTOR], 0;
	
	add eax, PXO_SIZE ; return alloc+PXO_SIZE
	
	pop ebx
	leave
	ret 4 ; one argument
	
; public void pxo_addref(pxobj*)
;  Increment reference count for a PXObject
; @param pxobj* A pointer to a buffer allocated with pxo_new
; @return void
pxo_addref:
	;
	push ebp
	mov ebp, esp
	push ebx
	
	; ebx = &refct
	mov ebx, dword[ebp+8]
	sub ebx, PXO_SIZE - PXO_OFFSET_REFCT
	
	; if (*ebx != dword 0) {
	cmp dword[ebx], 0
	je .nocmp
	
	; refcount++
		inc dword[ebx]
	
	; }
.nocmp:
	
	; return 0
	mov eax, 0
	
	pop ebx
	leave
	ret 4 ; one argument
	
; public void pxo_release(pxobj*)
;  Decrement reference count for a PXObject, free if necessary
; @param pxobj* A pointer to a buffer allocated with pxo_new
; @return void
pxo_release:
	;
	push ebp
	mov ebp, esp
	push ebx
	
	; ebx = &refct
	mov ebx, dword[ebp+8]
	sub ebx, PXO_SIZE - PXO_OFFSET_REFCT
	
	; if (*ebx != dword 0) {
	cmp dword[ebx], 0
	je .release_nocmp
	
	; refcount--
		dec dword[ebx]
	
	; if (*ebx == dword 0) {
		cmp dword[ebx], 0
		jne .release_nocmp
	
	; destructor
		push dword[ebp+8]
		add ebx, PXO_OFFSET_DESTRUCTOR - PXO_OFFSET_REFCT ; ebx = &destructor
		cmp dword[ebx], 0
		je .dtor_called
		
		;this->destruct(this)
		push dword[ebp+8]
		call dword[ebx]
		;ret
.dtor_called:
		sub ebx, PXO_OFFSET_DESTRUCTOR ; ebx = &base
		
	; free
		push ebx
		call pxl_rtl_free
	
	; }}
.release_nocmp:
	
	; return 0
	mov eax, 0
	
	pop ebx
	leave
	ret 4 ; one argument
	
	