include	clib.inc
include	conio.inc
include	dir.inc
include	io.inc
include	iost.inc
include	string.inc
include	errno.inc
include mouse.inc
include stdio.inc
include stdlib.inc
include malloc.inc
include	version.inc
include tview/tview.inc

	public	main
	public	tvflag
	public	fsflag
	public	searchstring
	public	configpath
	public	configfile
	public	tv_class
	public	tv_clversion
	public	tv_classsize
	public	tv_classrow
	public	_stklen

	extrn	_argc:word
	extrn	_argv:dword
	extrn	tvmem_offs:dword
	extrn	tvmem_size:dword
	extrn	IDD_MEMVIEW:dword

LINESIZE	= 64
MAXLINES	= 64
ID_COUNT 	= 11
ID_QUIT		= ID_COUNT
ID_VIEW		= ID_COUNT + 3

S_MCB		STRUC
mcb_flag	db ?
mcb_owner	dw ?
mcb_size	dw ?
mcb_unused	db 3 dup(?)
mcb_name	db 8 dup(?)
S_MCB		ENDS

_DATA	SEGMENT

_stklen	dw 	3000h

cp_info	label byte
	db	'Memview Version ',VERS?,' Copyright (c) 2012 Hjort Nidudsson',13,10
size_info = $ - offset cp_info

cp_usage label byte
	db	'Syntax is: MV [-options]',13,10
	db	13,10
	db	'  -?',9,9,   	'Print this screen',13,10
	db	'  -t',9,9,   	'View Text (default)',13,10
	db	'  -b',9,9,   	'View Binary',13,10
	db	'  -h',9,9,   	'View Hex',13,10
	db	'  -c',9,9,   	'Class View',13,10
	db	'  -q',9,9,	'quiet - no logo',13,10
size_usage = $ - offset cp_usage

cp_ini label byte
incbin <tview/default.ini>
size_ini = $ - offset cp_ini

option_q	db 0
mv_cfg		db 'mv.cfg',0
mv_ini		db 'mv.ini',0
arg_file	dd 0
arg_offset	dd 0
arg_size	dd 0
configpath	db WMAXPATH dup(0)
configfile	db WMAXPATH dup(0)

tvflag		db _TV_HEXOFFSET or _TV_USEMLINE or _TV_USESLINE
fsflag		db 0
searchstring	db 128 dup(0)
tv_clversion	dw CLVERSION
tv_classsize	dw CLCOUNT
tv_classrow	dw CLCOUNT
tv_class	S_CLASS CLCOUNT dup(<'byte',1,CLTYPE_BYTE,CLFORM_HEX>)
size_config	= $ - offset tvflag

M0000	db '00000 Interrupt vector table',0
M0400	db '00400 BIOS Data',0
M0500	db '00500 Data area',0
M0600	db '00600 MS-DOS 1.x Load address',0
M0700	db '00700 MS-DOS 2.+ Load address',0
format	db '%05lX           %7lu byte %04X',0
formatz	db '%7lu byte',0
o_list	dw ?
dialog	label dword
dlg_ax	dw ?
dlg_dx	dw ?

_DATA	ENDS

_TEXT	SEGMENT

normalize proc
	mov	cl,dh
	shl	dx,4
	add	ax,dx
	adc	cl,0
	shr	cl,4
	mov	dl,cl
	mov	dh,0
	ret
normalize endp

readmem proc
	push	si
	push	di
	push	bp
	mov	bx,o_list
	invoke	strcpy,[bx].ll_list,addr M0000
	mov	di,ax
	mov	dword ptr es:[di+LINESIZE-4],400h
	add	ax,LINESIZE
	invoke	strcpy, dx::ax, addr M0400
	mov	di,ax
	mov	dword ptr es:[di+LINESIZE-4],100h
	add	ax,LINESIZE
	invoke	strcpy, dx::ax, addr M0500
	mov	di,ax
	mov	dword ptr es:[di+LINESIZE-4],100h
	add	ax,LINESIZE
	invoke	strcpy, dx::ax, addr M0600
	mov	di,ax
	mov	dword ptr es:[di+LINESIZE-4],100h
	add	ax,LINESIZE
	invoke	strcpy, dx::ax, addr M0700
	mov	bp,ax
	add	ax,LINESIZE
	mov	si,ax
	mov	[bx.ll_count],5
	mov	ah,52h
	int	21h
	mov	di,es:[bx-2]	; segment of first memory control block
	movzx	eax,di
	sub	eax,0070h
	mov	es,dx
	mov	es:[bp+LINESIZE-4],eax
    readmem_loop:
	mov	es,di
	xor	ax,ax
	mov	bx,ax
	mov	cx,es:[bx].mcb_owner
	push	cx
	mov	dx,es:[bx].mcb_size
	mov	bp,dx
	call	normalize
	push	dx
	push	ax
	mov	dx,di
	xor	ax,ax
	call	normalize
	push	dx
	push	ax
	mov	bx,o_list
	mov	dx,word ptr [bx].ll_list+2
	mov	bx,si
	mov	es,dx
	movzx	eax,bp
	shl	eax,4
	mov	es:[bx+LINESIZE-4],eax
	mov	es,di
	mov	ax,offset format
	call	@sprintf
	add	sp,10
	mov	bx,o_list
	inc	[bx].ll_count
	cmp	[bx].ll_count,MAXLINES
	je	readmem_end
	xor	bx,bx
	mov	ax,es:[bx].mcb_owner
	mov	bx,o_list
	mov	dx,word ptr [bx].ll_list+2
	dec	ax
	cmp	ax,di
	je	readmem_copy
	inc	ax
	jz	readmem_free
	cmp	ax,8
	je	readmem_dos
	cmp	ax,6
	je	readmem_next
	cmp	ax,7
	je	readmem_next
	cmp	ax,0FFF7h
	jb	readmem_next
	mov	es,dx
	mov	ax,'83'
	mov	es:[si+7],ax
	mov	ax,'M6'
	mov	es:[si+9],ax
	mov	ax,'XA'
	mov	es:[si+11],ax
	jmp	readmem_next
    readmem_free:
	mov	es,dx
	mov	ax,'RF'
	mov	es:[si+6],ax
	mov	ax,'EE'
	mov	es:[si+8],ax
	jmp	readmem_next
    readmem_dos:
	mov	es,dx
	mov	ax,'OD'
	mov	es:[si+6],ax
	mov	al,'S'
	mov	es:[si+8],al
	jmp	readmem_next
    readmem_copy:
	mov	cx,8
	mov	ax,word ptr [bx].ll_list+2
	mov	es,di
	mov	bx,cx
	mov	dx,ds
	mov	ds,ax
	push	si
	add	si,6
    readmem_cpyl:
	mov	al,es:[bx]
	inc	bx
	or	al,al
	jz	readmem_endc
	mov	[si],al
	inc	si
	dec	cx
	jnz	readmem_cpyl
    readmem_endc:
	pop	si
	mov	ds,dx
    readmem_next:
	add	si,LINESIZE
	mov	es,di
	xor	bx,bx
	add	di,es:[bx].mcb_size
	inc	di
	mov	es,di
	mov	al,es:[bx].mcb_flag
	cmp	al,4Dh
	je	readmem_loop
    readmem_end:
	pop	bp
	pop	di
	pop	si
	ret
readmem endp

init_list proc
	push	si
	push	di
	push	bp
	push	ds
	cld
	mov	cx,ID_COUNT
	mov	bx,o_list
	mov	[bx].ll_numcel,0
	mov	ax,LINESIZE
	mul	[bx.ll_index]
	add	ax,word ptr [bx].ll_list
	mov	dx,word ptr [bx].ll_list+2
	mov	bp,bx
	les	di,dialog
	lea	di,[di+16].to_data	; &object[0].data
	mov	ds,dx			; DS:SI to list
	mov	si,ax
    event_list_loop:
	mov	ax,7F80h
	or	es:[di-7],al		; object.flag to unused
	cmp	byte ptr [si],0		; entry in use ?
	jz	event_list_next
	and	es:[di-7],ah
	inc	[bp.ll_numcel]
    event_list_next:
	mov	ax,si			; store pointer
	stosw
	mov	ax,ds
	stosw
	add	di,12			; next object
	add	si,LINESIZE		; next info line
	dec	cx
	jnz	event_list_loop
	pop	ds
	pop	bp
	pop	di
	pop	si
	ret
init_list endp

event_list proc dist
	call	init_list
	invoke	dlinit, dialog
	mov	ax,_C_NORMAL
	ret
event_list endp

event_view proc	dist
	mov	STDI.ios_flag,IO_MEMREAD
	mov	bx,o_list
	mov	ax,[bx].ll_celoff
	inc	ax
	shl	ax,4
	les	bx,dialog
	add	bx,ax
	pushm	es:[bx].to_data
	les	bx,es:[bx].to_data
	push	dword ptr es:[bx+LINESIZE-4]
	invoke	xtol, es::bx
	pop	ecx
	shr	ax,4
	shl	dx,4
	or	ah,dl
	push	ax
	push	0
	push	ax
	push	0
	push	ecx
	call	tview
	xor	ax,ax
	ret
event_view endp

cmmemory proc pascal uses si di
local	list:S_LOBJ
	lea	ax,list
	mov	o_list,ax
	invoke	malloc, LINESIZE * MAXLINES
	jz	cmmemory_ernomem
	stom	list.ll_list	; pointer to list buffer
	invoke	memzero, dx::ax, LINESIZE * MAXLINES
	invoke	rsopen, IDD_MEMVIEW
	jz	cmmemory_free
	stom	dialog
	invoke	dlshow, dx::ax
	fpmov	list.ll_proc,event_list
	fpmov	es:[bx+16+ID_VIEW*16].to_proc,event_view
	xor	ax,ax
	mov	list.ll_dcount,ID_COUNT	; number of cells (max)
	mov	list.ll_celoff,ID_COUNT	; cell offset
	mov	list.ll_dlgoff,ax	; start index in dialog
	mov	list.ll_numcel,ax	; number of visible cells
	mov	list.ll_count,ax	; total number of items in list
	mov	list.ll_index,ax	; index in list buffer
	call	readmem
	call	event_list
	int	12h
	mov	dx,1024
	mul	dx
	mov	si,dx
	mov	di,ax
	push	dx
	push	ax
	mov	bx,word ptr IDD_MEMVIEW
	mov	bx,[bx+6]
	add	bx,0E14h
	xor	cx,cx
	mov	dx,ds
	mov	ax,offset formatz
	call	@scputf
	mov	dx,_psp
	add	dx,2
	xor	ax,ax
	call	normalize
	push	dx
	push	ax
	inc	bh
	xor	cx,cx
	mov	ax,offset formatz
	call	@scputf
	pop	ax
	pop	dx
	sub	di,ax
	sbb	si,dx
	push	si
	push	di
	inc	bh
	mov	ax,offset formatz
	call	@scputf
	add	sp,8
    cmmemory_event:
    	invoke	dllevent, dialog, addr list
	jz	cmmemory_close
	cmp	ax,ID_COUNT
	ja	cmmemory_close
	call	event_view
	jz	cmmemory_event
    cmmemory_close:
	invoke	dlclose, dialog
    cmmemory_free:
	invoke	free, list.ll_list
    cmmemory_toend:
	ret
    cmmemory_ernomem:
	xor	ax,ax
	jmp	cmmemory_toend
cmmemory endp

print_usage:
	mov cx,size_info
	mov dx,offset cp_info
	call stderrmsg
	mov cx,size_usage
	mov dx,offset cp_usage
	call stderrmsg
	xor ax,ax
	ret

main	proc
	mov bx,word ptr _argv
	invoke strcpy, addr configpath, [bx]
	invoke strfn, dx::ax
	mov bx,ax
	mov byte ptr [bx-1],0
	invoke strfcat, addr configfile, addr configpath, addr mv_cfg
	.if func(osopen, dx::ax, _A_NORMAL, M_RDONLY, A_OPEN) != -1
	    push ax
	    invoke osread, ax, addr tvflag, size_config
	    call close
	.endif
	mov si,1
    arg_loop:
	cmp si,_argc
	jae init_info
	les bx,_argv
	mov ax,si
	inc si
	shl ax,2
	add bx,ax
	les bx,es:[bx]
	mov ax,es:[bx]
	cmp al,'?'
	je print_usage
	cmp al,'/'
	je options
	cmp al,'-'
	je options
	jmp print_usage
    options:
	mov al,ah
	or al,20h
	.if al == 't'
	    and tvflag,not (_TV_HEXVIEW or _TV_BINVIEW)
	.elseif al == 'b'
	    or tvflag,_TV_BINVIEW or _TV_HEXVIEW
	.elseif al == 'h'
	    or tvflag,_TV_HEXVIEW
	.elseif al == 'q'
	    inc option_q
	.elseif al == 'c'
	    or tvflag,_TV_CLASSVIEW
	.else
	    jmp print_usage
	.endif
	jmp arg_loop
    init_info:
	.if !option_q
	    mov cx,size_info
	    mov dx,offset cp_info
	    call stderrmsg
	.endif
      ifdef __MOUSE__
	call mouseinit
	call mouseon
      endif
	invoke strfcat,addr configfile,addr configpath,addr mv_ini
	.if func(access,dx::ax,0)
	    .if func(osopen, addr configfile, _A_NORMAL, M_WRONLY, A_CREATE or A_TRUNC) != -1
		push ax
		mov bx,ax
		or _osfile[bx],FH_TEXT
		invoke write,ax,addr cp_ini,size_ini
		call close
	    .endif
	.endif
	call cmmemory
      ifdef __MOUSE__
	call mouseoff
      endif
	invoke strfcat,addr configfile,addr configpath,addr mv_cfg
	.if func(osopen,dx::ax,_A_NORMAL,M_WRONLY,A_CREATE or A_TRUNC) != -1
	    push ax
	    invoke oswrite,ax,addr tvflag,size_config
	    call close
	.endif
	xor ax,ax
	ret
main	endp

_TEXT	ENDS

	END
