;MEM.ASM
code    segment
	assume	cs:code,ds:code
	org	100h
start:	jmp	begin
	db	5 dup ('stack	')
stack_area	dw 0

topline db      ' CURSOR LOCATION: 00000  CONTENTS: 00',32,32,32
help?	db	'(? FOR HELP)  '
find?	db	'FIND:',32,32,32,32,32,32,32,32,32 ;because I'm using Qedit...
jump?   db	'JUMP TO:',32,32,32,32,32,32
search  db	'SEARCHING... '
escmsg	db	0,'Press ESC to QUIT',0,0
	db	0,'Arrow keys - move cursor',32,32,32,32,32,32,32,32,32,32,32,32
	db	'(& Ctrl-Left Arrow & Ctrl-Right Arrow)',0
	db	0,'Home - left end of line',32,32,32,32,32,32,32,32,32,32,32,32,32
        db	'End - right end of line',0
	db	0,'Ctrl-Home - top of screen',32,32,32,32,32,32,32,32,32,32,32
	db	'Ctrl-End - bottom of screen',0
	db	0,'Ctrl-PgUp - beginning of memory',32,32,32,32,32
	db	'Ctrl-PgDn - end of memory',0
	db	0,'PgUp - previous screen',32,32,32,32,32,32,32,32,32,32,32,32,32,32
	db	'PgDn - next screen',0
	db	0,'Scroll Lock ON to scroll screen with Arrow Keys',0
	db	0,'Ctrl-F - Find string of characters',0
	db	0,'Ctrl-L - Continue to search for string',0
	db	0,'Ctrl-J - Jump to hexadecimal memory address',0
	db	0,0,'PRESS ANY KEY TO RETURN...',0

	db	'MEM.COM (c)1987 by Doug Cox '
findstr db	32,32,32,32,32,32,32,32
cursave dw      0
saves   dw      0
strcnt  dw      0
oldcur  dw      0
chars   dw      0
curptr	dw	0
toplef  dw      0
mode	db	0

begin:  lea     ax,stack_area
	mov	sp,ax
	mov	ah,0fh		;function to check screen mode
	int	10h
	cmp	al,0bh		;indicates display mode
	jb	notega		;if monochrome or CGA
	jmp	exit
notega: mov	mode,al 	;save it

	mov	ax,0b000h	;video memory address
	cmp	mode,7
	je	mono		;if monochrome display
	add	ax,800h 	;if CGA display
mono:	mov	es,ax
;save old cursor location
	call	getcur
        dec     dh
        mov     oldcur,dx       ;row in dh / col in dl
;save old screen
        mov     di,0
        lea     si,oldscr
oldlp0: mov	cx,80		;columns
oldlp:	mov	al,es:[di]
	mov	ds:[si],al
	inc	di
	inc	di
	inc	si
        loop    oldlp
	dec	dh		;rows
	jnz	oldlp0

        mov     ah,0            ;function to set screen mode
	mov	al,2		;code for 25x80 B&W
        int     10h             ;clear screen

	mov	ah,1		;function to change cursor shape
	mov	cx,7		;top & bottom row of cursor
	int	10h

;put top line on screen
	lea	si,topline
	mov	di,0		;location on screen
	mov	cx,53		;for count
	mov	ah,70		;for screen attribute
	call	movit		;move [si] into [di] & inc di cx times

;put memory contents on screen
memtop: mov	toplef,0	;memory location (& data segment)
	mov	curptr,0	;cursor offset location in memory
	mov	dx,0100h	;row 1/column 0
        call    movcur          ;initialize cursor at top left
newscrn:mov	ah,7		;screen attribute
	mov	si,0		;offset of memory to put on screen
	mov	di,160		;top left of screen
	cmp	toplef,0ff8ch	;last possible toplef
	jnc	atend		;to not write past 0fffffh
        mov     cx,1920         ;80*24 screen
	mov	ds,toplef	;toplef is also data segment beginning
	call	movit		;lodsb, stosw, loop
new2:   mov	dx,cs
	mov	ds,dx		;put data seg back

;put cursor location & contents at screen top
locont: mov	dx,toplef	;data segment
        mov     bx,dx           ;for first binihex call
        mov     cl,4
	shl	dx,cl
        add     dx,curptr       ;offset addr
        jnc     locont2
	add	bx,1000h	;increment 5th byte
locont2:mov	di,36		;location on screen
	mov	ch,1		;for count
	call	binihex 	;show 5th byte of location
	mov	bx,dx
	mov	ch,4
	call	binihex 	;show other 4 bytes of location

	mov	bx,curptr	;cursor location in memory
	mov	ds,toplef
	mov	bh,[bx] 	;contents
	mov	cx,cs
	mov	ds,cx
	mov	di,70		;screen loc
	mov	ch,2		;2 chars
	call	binihex
;read keyboard
wt:   mov ah,0        ;function to read keyboard
	int	16h		;program stays here, mostly
        cmp     al,0
	je	extended_code

	call	case		;to jump to following subroutines
	db	ctrls/3
diff2	equ	$
	db	10	;^J
	dw	jump
        db      6       ;^F
	dw	find
	db	12	;^L
	dw	again
	db	3fh	;'?'
	dw	emenu
	db	1bh	;ESC
	dw	exit
ctrls   equ     $-diff2
        dw      wt

;if at last possible screen of memory
atend:	mov	dx,toplef
	mov	ds,dx		;don't change ds before getting ds:[toplef]
	mov	cl,4
	shl	dx,cl		;rotate left 1 byte
	mov	cx,0ffffh
	sub	cx,dx
	inc	cx
        mov     dx,1920         ;80*24
	sub	dx,cx
        call    movit           ;put memory on screen, but put blanks past ffffh
        mov	cx,dx
	mov	ax,0700h	;attribute & blank
        rep     stosw
	jmp	new2

extended_code:
        mov     al,ah
        call    case
	db	extends/3	;number of routines to check
diff1	equ	$
        db      71      ;Home
	dw	lftside
	db	79	;End
	dw	rtside
	db	73	;PgUp
	dw	prev
	db	81	;PgDn
	dw	next
	db	72	;Up arrow
	dw	curup
	db	80	;Down arrow
	dw	curdn
	db	75	;Left arrow
	dw	curlft
	db	77	;Right arrow
	dw	currt
	db	116	;Ctrl-Right arrow
	dw	movrt
	db	115	;Ctrl-Left arrow
	dw	movlft
        db      132     ;Ctrl-PgUp
        dw      memtop
        db      118     ;Ctrl-PgDn
	dw	goend
	db	119	;Ctrl-Home
	dw	scrtop
	db	117	;Ctrl-End
	dw	scrbot
extends equ     $-diff1
        dw      wt    ;go there if not one of above


;'?'
emenu:	call	savcur
        lea     si,escmsg
        call    menwrt
	cmp	al,1bh		;ESC
	je	jmpexit
	cmp	al,3		;^C
	je	jmpexit
	mov	dx,cursave
	call	movcur
        jmp     newscrn

jmpexit:jmp	exit

;Ctrl-Home
scrtop: call	getcur		;row in dh & col in dl
	mov	al,80
        mul     dh
	sub	curptr,ax
	add	curptr,80	;because top row here is 1 not 0
        mov     dh,1            ;top row
        call    movcur
        jmp     locont

;Ctrl-End
scrbot: call    getcur
	mov	al,24		;bottom row
	sub	al,dh		;cursor row
	mov	ah,80
	mul	ah
	add	curptr,ax
;now test for end of memory
        mov     ax,toplef
	mov	cl,4
	shl	ax,cl
	add	ax,curptr
	jnc	scrbot2 	;if not past 0fffffh
	call	curend
	jmp	locont

scrbot2:mov     dh,24           ;bottom row
        call    movcur
        jmp     locont

;Ctrl-PgDn
goend:  mov	toplef,0ff8ch	;last possible toplef
	mov	curptr,1855	;80*23+15
	mov	dx,180fh	;row 24 col 15
	call	movcur
	jmp	newscrn

;Home
lftside:call	getcur
	push	dx
        mov     dh,0
	sub	curptr,dx	;decrement curptr to 1st col
	pop	dx
	mov	dl,0
        call    movcur
	jmp	locont

;End
rtside: mov	ax,toplef
	cmp	ax,0ff8ch	;last screen in memory
        jc      rtside2
        mov     cl,4
	shl	ax,cl
	add	ax,curptr
	cmp	ax,0fff0h	;beginning of last line in memory
	jc	rtside2
        call    curend
	jmp	locont

rtside2:call    getcur
	mov	al,79
	sub	al,dl
	mov	ah,0
        add     curptr,ax
        mov     dl,79
	call	movcur
	jmp	locont

;PgUp
prev:   cmp	toplef,0
	je	jmpwait
	cmp	toplef,78h	;1920d shifted right (24*80) screen
	jnc	prev2		;if not within 24*80 of beginning of memory
	jmp	memtop
prev2:  sub     toplef,78h
gonew:	jmp	newscrn

;PgDn
next:   mov	dx,toplef
	cmp	dx,0ff8ch	;last possible toplef
	jnc	jmpwait 	;if at end of memory
	add	dx,78h		;1920d shifted right
	mov	toplef,dx
	mov	cl,4
	shl	dx,cl
	add	dx,curptr
	jnc	gonew		;if cursor won't be past 0fffffh
	call	curend
        jmp     newscrn

curjmp: jmp	locont

;Left Arrow
curlft: call	lftcur
	jnc	curjmp
	jmp	newscrn

;Right Arrow
currt:	call	rtcur
	jnc	curjmp
	jmp	newscrn

;Ctrl-Right Arrow
movrt:	mov	cx,10
rtlp:	push	cx
	call	rtcur
	pop	cx
        loop    rtlp
	jmp	newscrn

;Ctrl-Left Arrow
movlft: mov	cx,10
lftlp:	push	cx
	call	lftcur
	pop	cx
        loop    lftlp
        jmp     newscrn

jmpwait:jmp     wt

;Up Arrow
curup:	call	scrlock
	jnz	up		;if ScrollLock is ON
	call	getcur
	cmp	dh,1
	je	curup2		;if on top line
	dec	dh
	call	movcur
        sub     curptr,80
        jmp     locont

curup2: cmp	toplef,0
	jz	jmpwait
	sub	toplef,5
	jmp	newscrn

;ScrollLock ON
up:	cmp	toplef,0
	je	jmpwait 	;if at beginning of memory
	sub	toplef,5
	call	getcur
	cmp	dh,24
	je	downend 	;If at bottom
	inc	dh
	call	movcur
        add     curptr,80       ;because of toplef change
        jmp     newscrn

jmpwait2:jmp    wt

;Down Arrow
curdn:	call	scrlock
	jnz	down		;if ScrollLock is ON
	cmp	toplef,0ff8ch
	jc	curdn3		;if not at last screen in memory
	mov	ax,toplef
	mov	cl,4
	shl	ax,cl
	add	ax,curptr
	add	ax,80
	jc	jmpwait2	;if move would put cursor past 0fffffh
curdn3: call    getcur
	cmp	dh,24
	je	curdn2		;if on bottom line
	inc	dh
	call	movcur
	add	curptr,80
        jmp     locont

curdn2: add	toplef,5
	cmp	toplef,0ff8ch
	jc	downend 	;if not at end of memory
        call    curend
	jmp	newscrn 	;don't need to increment curptr

;ScrollLock ON
down:   cmp     toplef,0ff8ch
	jnc	jmpwait2	;if at end of memory
	add	toplef,5	;80 shifted right
	call	getcur
	cmp	dh,1
	je	downend 	;if cursor is on top line
	dec	dh
	call	movcur
        sub     curptr,80       ;because of toplef change
downend:jmp     newscrn

;jump to input hex address
jump:	call	savcur
	lea	si,jump?
	call	top
	mov	bp,96		;for screen location in getstr
	call	getstr		;get input
	cmp	dx,0		;input string length
	jnz	jumpsk
	jmp	find15		;if no input
jumpsk: mov	si,di		;just after last input char on screen
	mov	ax,0
	mov	di,ax		;for offset address
	mov	bx,ax		;for segment address
	mov	dx,0004h	;dh for cl & dl for loop
;get offset address
jumplp: dec	si
	dec	si
	cmp	si,96		;input screen location
	jl	jumpxit 	;if no good input
        mov     al,es:[si]
	call	hex2bin 	;convert to binary
	jc	jumplp		;if not hexadecimal
	mov	cl,dh
	mov	ah,0		;rol changes it
	add	dh,4		;to rol ax to next nibble to left
        rol     ax,cl
	or	di,ax		;ta daa!
	dec	dl
	jnz	jumplp
;get segment address
        dec     si
	dec	si
	mov	al,es:[si]
	call	hex2bin
	jc	jumpxit
	mov	ah,0
        or      bx,ax
jumpxit:mov	saves,es
	jmp	find16		;to jump to address


find:   call    savcur
	lea	si,find?
	call	top
	mov	bp,90
	call	getstr
;put string in findstr
	cmp	dx,0
	jne	find12
	jmp	find15		;if no input
find12: mov	si,90
	lea	di,findstr
	mov	cx,8
find5:  mov     al,es:[si]
	mov	ds:[di],al
        inc     si
	inc	si
        inc     di
	loop	find5
;write 'SEARCHING...'
	lea	si,search
	mov	cx,12
	mov	di,108
	mov	ah,7
	call	movit
;search for string
	mov	di,0		;beginning of memory offset
        mov     bx,0
	mov	saves,es
	mov	strcnt,dx	;save it for again:
	mov	cx,0ffffh	;for scasb count
;jumped to from again:
find14: mov	es,bx		;beginning of memory segment
	dec	dx		;because cmpsb doesn't look at 1st char
	jnz	find10
;if only one char to find
	mov	al,findstr
	repnz	scasb
	jmp	short find11
;subroutine to increment segment
find9:	mov	di,0
	mov	cx,0ffffh
	add	bx,1000h
	mov	es,bx		;to next segment
	jc	find7		;if past last segment
;big find loop
find10: lea     si,findstr
	mov	al,[si]
	repnz	scasb		;repeat until z=true or cx=0
	jnz	find9		;if string not found in current segment (cx=0)
	mov	bp,di		;save it
	push	cx
	mov	cx,dx		;for cmpsb count
	inc	si		;point to remainder of string
	repz	cmpsb		;compare strings (repeat until z=false or cx=0)
	pop	cx		;get scasb count back
	mov	di,bp		;get it back
	jnz	find10		;if string not found
;jump to address
find11: dec	di		;offset addr
	mov	cl,4
	rol	bx,cl		;move segment addr to right-most byte
	and	bx,0fh		;clear all but right-most byte
find16: mov	dx,bx		;for div by word
	mov	ax,di
	mov	cx,80
	div	cx		;remainder is in dx (& dl = column)
	mov	curptr,dx	;column (dh=0)
	sub	di,curptr	;to get toplef
	jnc	find13
	dec	bx
find13: mov     cl,4
	shr	di,cl
	mov	cl,4
	ror	bx,cl		;back to previous shape
	add	di,bx
	mov	toplef,di	;ta-daa!
	mov	dh,1		;put cursor on first row
	mov	cursave,dx	;for movcur
;clear message & exit
find7:	mov	es,saves
find15: mov	ax,700h
	mov	di,108
	mov	cx,12
	rep	stosw
;put '? FOR HELP' back
	lea	si,help?
	call	top
	mov	dx,cursave
	call	movcur
        jmp     newscrn

;look for string again
again:	call	savcur
	lea	si,search
	mov	cx,12
	mov	di,108
	mov	ah,7
	call	movit
	mov	dx,strcnt
	mov	saves,es
	mov	di,toplef
	mov	bx,di
	mov	cl,4
	rol	di,cl
	and	di,0fff0h	;clear right byte
	add	di,curptr	;offset addr
	inc	di		;start looking 1 byte beyond current location
        jnc     againsk
	add	bx,1000h
againsk:and	bx,0f000h	;clear all but left byte (for seg addr)
	mov	cx,0ffffh
	sub	cx,di		;for count to end of current segment
        jmp     find14


;return to DOS
exit:   mov     bh,0            ;page 0
	mov	ah,bh		;function to set screen mode
	mov	al,mode
	int	10h
;restore old cursor location
        mov     dx,oldcur       ;old cursor location
	dec	dh
	call	movcur
;restore old screen
        inc     dh
        mov     di,0
	lea	si,oldscr
exitlp: mov	cx,80		;columns
	mov	ah,7
        call    movit           ;move [si] into [di] & inc di cx times
	dec	dh		;rows
	jnz	exitlp
        int     20h             ;return to DOS

;***CALLED ROUTINES***

top	proc	near
	mov	cx,14
	mov	di,78
	mov	ah,70
        call    movit
	ret
top	endp

movit   proc    near
movlp:  lodsb
	stosw
	loop	movlp
	ret
movit	endp

;save cursor location
savcur  proc    near
        call    getcur
	mov	cursave,dx
	mov	dx,1900h	;row 25,col 0
	jmp	short movcur
savcur	endp

;move cursor to row in dh & col in dl
movcur  proc    near
	mov	ah,2
	jmp	short cur
movcur  endp

;put row in dh & col in dl
getcur  proc	near
	mov	ah,3
cur:	mov	bh,0
	int	10h
	ret
getcur  endp

;to jump to a subroutine (semi-trick)
case	proc	near
	pop	bx	;RET address
	mov	cl,[bx] ;number of comparisons to make
	mov	ch,0
caslop: inc	bx	;to a db
	cmp	al,[bx]
	je	go
	inc	bx	;to a dw
	inc	bx
	loop	caslop
go:	inc	bx	;to a dw
	mov	bx,[bx]
        jmp     bx
case	endp

;to change binary to hex
binihex proc	near
binlp:	mov	cl,4
	rol	bx,cl		;move left byte to right side
	mov	al,bl		;this is necessary
	and	al,0fh		;clear left byte
	add	al,30h
	cmp	al,3ah
	jl	num
	add	al,7
num:	stosb
        inc     di
        dec     ch
	jnz	binlp
	ret
binihex endp

;convert al from hex to binary
hex2bin proc	near
	cmp	al,'0'
	jl	badhex
	cmp	al,'9'
	jle	hex2		;if number
	and	al,0dfh 	;make letters uppercase
	cmp	al,'A'
	jl	badhex
	cmp	al,'F'
	jg	badhex
	sub	al,7
hex2:	sub	al,30h
	clc
        ret
badhex: stc
	ret
hex2bin endp

;to put cursor at 0fffffh
curend  proc    near
	mov	dx,toplef
        mov     cl,4
	shl	dx,cl
	mov	ax,0ffffh
	sub	ax,dx
	mov	curptr,ax
        mov     cl,80
	div	cl
	inc	al
        mov     dh,al           ;quotient (answer) (for row)
	mov	dl,ah		;remainder (for column)
	jmp	short movcur
curend	endp

lftcur	proc	near
	call	getcur
	cmp	dx,0100h
	je	lfttop		 ;if at top left of screen
	dec	curptr
	cmp	dl,0
	je	prevlin
	dec	dl
	jmp	short lftmov
prevlin:dec	dh
	mov	dl,79
lftmov: call	movcur
        clc
lftxit: ret
lftcur  endp

lfttop: cmp	toplef,0
	je	lftxit		;if at beginning of memory
	mov	dl,79
	call	movcur
	sub	toplef,5	;80d shifted right
	add	curptr,79	;because of toplef change
        stc
        ret

rtcur   proc    near
	cmp	toplef,0ff8ch
	jc	rtcur2		;if not at last screen
	mov	ax,toplef
        mov     cl,4
	shl	ax,cl
	inc	ax
	add	ax,curptr
        jc      rtexit          ;if cursor would go beyond 0fffffh
rtcur2: inc     curptr
	call	getcur
	cmp	dl,79
	je	nexlin
	inc	dl
	jmp	short rtmov
nexlin: cmp	dh,24
	je	rtbot
	inc	dh
	mov	dl,0
rtmov:	call	movcur
        clc
rtexit: ret
rtcur   endp

rtbot:  cmp     toplef,0ff8ch   ;last toplef in memory
	jnc	rtexit
	mov	dl,0
	call	movcur
        add     toplef,5
	sub	curptr,80	;because of toplef change
        stc
        ret

scrlock proc	near		;check ScrollLock on or off
	push	es
	mov	ax,0
        mov     es,ax
	test	byte ptr es:[417h],00010000b
	pop	es
	ret
scrlock endp

;get string to search for
getstr	proc	near
        mov     dx,0            ;for count in cmpsb, below
	mov	di,bp
find2:  mov     ah,0            ;read keyboard function
	int	16h
	cmp	ah,14		;Backspace key
	je	find20
	cmp	ah,83		;Del key
	je	find20
	cmp	ah,75		;Left Arrow key
	je	find20
	cmp	al,8
	jne	find3		;if no erase input
;erase char
find20: cmp	di,bp
	je	find2		;don't erase if at beginning of input location
	dec	di
	dec	di
	dec	dx
        mov     word ptr es:[di],4620h  ;attribute & space
	jmp	short find2
;put char on screen
find3:  cmp     di,106          ;last input space
	je	find4		;exit
	cmp	al,0dh		;Enter key
	je	find4		;exit
        stosb
        inc     di
	inc	dx
	jmp	short find2
find4:	ret
getstr  endp

menwrt	proc	near
        mov     di,160
	mov	ax,0
	mov	cx,5
	rep	stosw
        mov     ah,7            ;for attribute
        mov     dx,24           ;rows
menlp0: mov	cx,80		;columns
menlp:	lodsb
	cmp	al,0
	je	mensk
	stosw
        loop    menlp
mensk:	rep	stosw
	cmp	si,0
	jne	mensk2
	add	di,160
mensk2: dec	dx
	jnz	menlp0
	mov	ah,0
	int	16h		;read keyboard
        ret
menwrt	endp

oldscr	equ	$
code    ends
	end	start
