;+
;
; E11 loadable module for accessing private extended memory from FORTRAN.
;
; By John Wilson <wilson@dbit.com>.
;
; Copyright (C) 1999-2008 by Digby's Bitpile, Inc.  All rights reserved.
;
; 10/07/1999	JMBW	Created (from sample E11 DLL).
; 01/16/2000	JMBW	Modified to just handle a memory buffer, no interrupts
;			or anything fancy like that.
; 01/20/2000	JMBW	Added IDATA autoincrementing data port.
; 01/18/2002	JMBW	Debugged WEP now that it's really called on exit.
; 12/31/2006	JMBW	32-bit version.
; 09/04/2008	JMBW	Fixed DATO parameter order.
;
;-
	.386
	.model	flat
	.code
;
	extrn	_GetCSRBlock@20:near,_RetCSRBlock@4:near
	extrn	_GetMemory@4:near,_RetMemory@4:near
;
csr=	17777100o		;base CSR address in PDP-11 space
memsiz=	8d*1048576d		;size of extended memory block
;
DLL_PROCESS_ATTACH=1		;library is newly loaded
DLL_PROCESS_DETACH=0		;bed time
;
.assume	macro	cond
if &cond
else
	.err	Assumption &cond is false
endif
	endm
;+
;
; Init/shutdown entry point.
; BOOL DllMain(HINSTANCE hInstDLL,DWORD fdwReason,LPVOID lpvReserved)
;
;-
start:	mov	eax,[esp+04h+04h] ;get fdwReason
	dec	eax		;newly loaded?
.assume <DLL_PROCESS_ATTACH eq 1>
	jnz	short init2	;no
	; set up CSR handler
;;; lpvReserved points at counted command line string
;;; should parse "CSR=xxxxxx" and/or "SIZE=xxxx" instead of hard-coding those

	; GetCSRBlock(ulong iobase,ulong nwords,uint (*dati)(),void (*dato)(),
	; void (*init)()
	push	offset init	;INIT routine
	push	offset dato	;DATO routine
	push	offset dati	;DATI routine
	push	4		;4 registers
	push	csr		;base I/O address
	call	_GetCSRBlock@20	;alloc block of CSR addresses
	test	eax,eax		;success?
	jz	short init4	;no
	mov	ds:csrhnd,eax	;save handle
	push	memsiz		;size of memory block
	call	_GetMemory@4	;allocate and zero memory
	test	eax,eax		;success?
	jz	short init3	;no, give up
	mov	ds:mem,eax	;save it
init1:	mov	eax,-1		;success
	ret	3*4		;EAX=0 if failed, NZ if succeeded
init2:	inc	eax		;unloading?
.assume <DLL_PROCESS_DETACH eq 0>
	jnz	short init1	;no, return success for others (no op)
	push	dword ptr ds:mem ;memory block
	call	_RetMemory@4	;return it
init3:	push	dword ptr ds:csrhnd ;CSR block handle
	call	_RetCSRBlock@4	;flush it
init4:	xor	eax,eax		;error return
	ret	3*4
;
dati:	; handle DATI cycles (register reads)
	; dati(uint32 addr)
	push	ebx		;save
	mov	ebx,[esp+04h+04h] ;get address
	sub	ebx,csr		;subtract base addr
	and	bl,not 1	;force even
	call	dword ptr ds:input[ebx+ebx] ;handle it (preserve EBP/ESI/EDI)
	pop	ebx		;restore
	ret	4
;
dato:	; handle DATO(B) cycles (register writes)
	; (uint32 addr, uint32 value, uint32 datob)
	push	ebx		;save
	mov	ebx,[esp+04h+04h] ;get address
	mov	edx,ebx		;copy
	sub	ebx,csr		;subtract base addr
	and	bl,not 1	;force even
	mov	eax,[esp+04h+04h+04h] ;get data
	mov	ecx,[esp+04h+04h+08h] ;DATOB flag
	call	dword ptr ds:output[ebx+ebx] ;handle it (preserve EBP/ESI/EDI)
				;AX=data written, ECX=DATOB flag, EDX=address
	pop	ebx		;restore
	ret	3*4
;
init:	; handle bus init (RESET instruction)
	; (uint32 addr)
	xor	eax,eax		;load 0
	mov	ds:addrs,eax	;clear addr ports
	ret	4
;+
;
; +00	data port
;
;  15         0
; +------------+
; |    DATA    |
; +------------+
;
; DATA	RW	One-word window to word of extended memory addressed by ALOW
;		and AHIGH.  Byte accesses are OK and read/write only that
;		particular byte.  If ALOW/AHIGH select a byte or word that's
;		beyond the end of the extended memory block, writes to DATA are
;		ignored and reads from DATA return 0.
;
;-
datai:	; input from data port
	mov	ebx,ds:addrs	;get pointer into memory
	xor	eax,eax		;load 0 in case NXM
	cmp	ebx,memsiz	;off end of memory?
	jae	short datai1	;yes
	add	ebx,ds:mem	;add base of block
	movzx	eax,word ptr [ebx] ;fetch word at addressed location
datai1:	ret
;
datao:	; output to data port
	mov	ebx,ds:addrs	;get pointer into memory
	cmp	ebx,memsiz	;off end of memory?  (MEMSIZ assumed even)
	jae	short datao3	;yes, ignore the write
	add	ebx,ds:mem	;add base of block
	jecxz	short datao2	;DATO, easy
	test	dl,1		;DATOB, to high byte?
	jz	short datao1
	mov	[ebx+1],ah	;yes, save
	ret
datao1:	mov	[ebx],al	;no, low byte
	ret
datao2:	mov	[ebx],ax	;store a word
datao3:	ret
;+
;
; +02	autoincrementing data port
;
;  15          0
; +-------------+
; |    IDATA    |
; +-------------+
;
; IDATA	RW	One-word window to word of extended memory addressed by ALOW
;		and AHIGH.  AHIGH'ALOW are incremented (as a single 32-bit
;		quantity, the LSB is always 0) by 2 after the access.  Byte
;		accesses are OK and read/write only that particular byte, just
;		like the DATA register, but the resulting autoincrement
;		probably won't be what you want.  If ALOW/AHIGH select a byte
;		or word that's beyond the end of the extended memory block,
;		writes to DATA are ignored and reads from DATA return 0, and
;		AHIGH'ALOW aren't incremented.
;
;-
idati:	; input from autoincrementing data port
	mov	ebx,ds:addrs	;get pointer into memory
	xor	eax,eax		;load 0 in case NXM
	cmp	ebx,memsiz	;off end of memory?
	jae	short idati1	;yes
	add	ebx,ds:mem	;add base of block
	movzx	eax,word ptr [ebx] ;fetch word at addressed location
	add	dword ptr ds:addrs,2 ;increment address
idati1:	ret
;
idato:	; output to data port
	mov	ebx,ds:addrs	;get pointer into memory
	cmp	ebx,memsiz	;off end of memory?  (MEMSIZ assumed even)
	jae	short idato4	;yes, ignore the write
	add	ebx,ds:mem	;add base of block
	jecxz	short idato2	;DATO, easy
	test	dl,1		;DATOB, to high byte?
	jz	short idato1
	mov	[ebx+1],ah	;yes, save
	jmp	short idato3
idato1:	mov	[ebx],al	;no, low byte
	jmp	short idato3
idato2:	mov	[ebx],ax	;store a word
idato3:	add	dword ptr ds:addrs,2 ;increment address
idato4:	ret
;+
;
; +04	low word of address
;
;  15         1 0
; +------------+-+
; |    ALOW    |/|
; +------------+-+
;
; ALOW	RW	Low 16 bits (bit 0 masked off) of extended memory address.
;
;-
alowi:	mov	eax,ds:addrs	;fetch value (junk in LH)
	ret
alowo:	and	al,not 1	;mask off low bit
	mov	word ptr ds:addrs,ax ;save low word of value
	ret
;+
;
; +06	high word of address
;
;  15          0
; +-------------+
; |    AHIGH    |
; +-------------+
;
; AHIGH	RW	High 16 bits of extended memory address.
;
;-
ahighi:	movzx	eax,word ptr ds:addrs+2 ;fetch value
	ret
ahigho:	mov	word ptr ds:addrs+2,ax ;save high word of value
	ret
;
	.data
;
; Register dispatch table:
input	dd	datai,idati,alowi,ahighi
output	dd	datao,idato,alowo,ahigho
;
addrs	dd	0		;extended address (forced even)
;
	.data?
;
csrhnd	dd	1 dup(?)	;handle from GetCSRBlock for our CSRs
mem	dd	1 dup(?)	;address of our memory
;
	end	start
