	org	256

	jmp	Begin

	align	8
; ============================================================================================
; Returns 16 bit segment of SMBIOS entry point

;       ENTRY:  Nothing

;       LEAVE:  AX = Segment of SMBIOS or zero if not found. 

;       FLAGS:  CF = Signature not found, NC otherwise 
; --------------------------------------------------------------------------------------------

   SMBIOS_PNTR	equ	BP + 8
   
  Find_SMBIOS:
	push	ds
	push	bx			; Preserve essential
	push	si
	
    ; Establish DS:BX to point to base of BIOS code
    
	mov	ax, 0xf000
	mov	ds, ax			; Segment where table lives
	xor	bx, bx			; Initial pointer
	mov	eax, '_SM_'		; Scan buffer for this signature

    ; Loop has maximum of 4096 interations. As table is probably at top of buffer, cycling
    ; though it backwards saves time. In my test bed, BOCH's 2.6.5 BIOS-bochs-latest it was
    ; 1,451 interations.
	
   .L0: sub	bx, 16			; Bump pointer to previous page
	jnz	.J0

    ; Return NULL in AX and set CF. Either AX or flag can be tested on return.
    
	mov	ax, bx
	stc
	jmp	.Done
	
    ; Did we find signature at this page
       
   .J0: cmp	[bx], eax
	jnz	.L0			; NZ, keep looking
	
    ; Calculate checksum to verify position
    
	mov	cx, 15
	mov	ax, cx
	mov	si, bx			; DS:SI = Table entry point
	
    ; Compute checksum on next 15 bytes
    
   .L1: lodsb
	add	ah, al
	loop	.L1
	
	or	ah, ah
	jnz	.L0			; Invalid, try to find another occurence

    ; As entry point is page aligned, we can do this to determine segment.
	
	shr	bx, 4
	mov	ax, ds
	add	ax, bx
	clc				; NC, found signature
	
   .Done:
	pop	si
	pop	bx			; Restore essential
	pop	ds
	
	ret	

	align	8
; ============================================================================================
; Search for first occurence of type and optionally, each additional occurence

;       ENTRY:  DL = Type ( 0 - FF )
;               DH = NULL, search from first entry in table, FF = Search for next DL ignored

;       LEAVE:  AX = Pointer to strings. May be pointing to double NULL's
;                  = Total size of structures if CY

;       FLAGS:  NC = Success, CY otherwise
; --------------------------------------------------------------------------------------------

  EP_Struct	dw	0		; Structure Table Entry Point (Segment) page aligned
     Table	dd	0
		dw	-1

  FindStructure:

   ARG1    equ	BP + 4
   ARG2    equ	BP + 6
   ARG3    equ	BP + 8

	push	bp
	mov	bp, sp			; Empty procedure frame to address arguments

    ; Preserve essential registers. My procedures are fashioned somewhat after M$ specs
    ; wherein the only volatile registers are AX, CX & DX. All others are presereved.

	push	si
	push	di
	push	es
	push	ds

    ; Address local data to determine if procedure has been called before

	mov	si, EP_Struct		; Point to Table Entry Point
	mov	di, si			; For STOSW instruction
	lodsw
	or	ax, ax			; Has address been established
	jnz	.Ok

    ; First time, EP_Struct & Table must be initialized

	call	Find_SMBIOS
	jnc	@F			; CF set if SMBIOS Entry Point struct wasn't found.
	dec	ax			; Set error code
	jmp	.Exit			; Exit

    ; Save pointer to Entry Point data and convert 32 bit Table address to long pointer

    @@: stosw				; Save SMBIOS struct segment
	push	ds
	mov	ds, ax
	mov	eax, [24]		; 32 bit Table address
	pop	ds
	test	ax, 15			; Is address page aligned
	jz	@F

    ; I going to assume first entry is page aligned, but just in case this will prevent
    ; routine going bonkers.

	and	ax, 15
	stc
	jmp	.Exit

    @@: shr	eax, 4			; Convert to 16 bit segment pointer
	inc	di
	inc	di
	stosw				; Save in high order bytes of Table

   .Ok: lds	si, [si]		; Set DS:SI to first or previously found struct
	push	ds
	pop	es
	inc	dh
	jnz	@F

    ; This forces procedure to ignore anything passed by caller and search for next occurence
    ; of AL

	mov	ax, [si]
	mov	dl, al

    ; This is here so code can naturally fall through to here when searching for duplicates
    ; of the same type.

   .L0: shr	ax, 8
	add	si, ax
	xor	al, al
	cmp	[si], ax
	jz	@F - 2

    ; Detetected a string, so keep scanning for all occurences till double NULL encountered

	or	cx, -1
	mov	di, si

	repnz	scasb
	cmp	[di], al
	jnz	$ - 4

	mov	si, di			; Update SI
	jmp	$ + 3			; Only want to bump SI once

    ; bump over double NULL's

	inc	si
	inc	si

    @@: mov	ax, [si]		; AH = Length, AL = Type
	cmp	al, dl
	jnz	@F			; ZF On

	shr	ax, 8			; Move length into AL
	add	ax, si			; AX points to strings if applicable.
	jmp	.Exit			; Exit with CF cleared.

    @@: cmp	al, 127
	jnz	.L0

    ; As we've come to the end, re-set so next iteration will start from beginning

	mov	ax, si			; AX points to beginning of last structure
	xor	si, si
	stc				; Set error condition

   .Exit:
	pop	ds
	mov	word [Table], si	; Update position for next interation
	pop	es
	pop	di
	pop	si

	leave				; Kill procdure frame
	ret

	align	32
; ============================================================================================
; --------------------------------------------------------------------------------------------

  Begin:
	mov	dx, 8
    @@: call	FindStructure
	jc	@F

	push	ds
	lds	cx, [Table]
	pop	ds
	or	dx, -1
	jmp	@b

    @@: int	32
