; ---- PROGETTO DI CALCOLATORI ELETTRONICI ----
;       Merli Matteo
;  
;  Avim -- Editor di file di testo 
;          che assomiglia vagamente a Vim
;

IDEAL  ; Setta la modalita' ideal per l'assemblatore


;Costanti

LINEWIDTH       EQU 80          ;Lunghezza delle linee
SCREENLENGTH    EQU 24          ;Numero di righe a video
MAXLINES        EQU 8096        ;Dimensione dell'array di puntatori alle linee
BUFFERLENGTH    EQU 1200h       ;Lunghezza del buffer
BELL            EQU 7           ;Alcuni caratteri utili
BS              EQU 8
HT              EQU 9
LF              EQU 10
CR              EQU 13
CRLF            EQU 0A0Dh
CTRL_Z          EQU 26
ESCAPE          EQU 27
DEL             EQU 127

;Crea una stringa il cui primo byte rappresenta 
; la sua lunghezza
MACRO String Name, Text
	LOCAL	LL
Name    db LL-Name-1, Text
LL:
ENDM


;Macro definite per convenienza
MACRO B? Name, Length
Name db Length dup(?)
ENDM

;Come B? ma per contenere word
MACRO W? Name, Length
Name dw	Length dup(?)
ENDM


MODEL TINY 
STACK 256
LOCALS

CODESEG


Start1:
jmp Start

colorAttributes dw 7009h        ;Default, colore del testo e attributo video
colorAttrib1    dw 0779h        ;Colori invertiti
colorAttrib2    dw 01e0fh       ;Altra modalita' di colore
colorMode?	db 0		;Modalita' di colore
tabSize         db 8            ;Dimensione del tab
inserting?      db -1           ;Vero se siamo in Insert mode
commandMode?    db -1           ;Vero se siamo in modalita' comandi
autoIndent?     db -1           ;Vero se simo nel mode autoindent
startInText?    db 0            ;Setto a vero per iniziare in modalita' testo
zLMargin        db 8            ;Setta il margine sinistro
zRMargin        db LINEWIDTH-8  ;Setta il margine destro

;Strings

String cantOpenMsg,     'Impossibile aprire il file.'
String rdErrorMsg,      'Errore nella lettura del file.'
String fileErrorMsg,    'Impossibile salvare il file.'
String diskFullMsg,     'Disco pieno.'
String outOfMemoryMsg,  'File troppo grande.'    
String mallocErrMsg,	'Impossibile allocare memoria!!'
String setTabsMsg,      'Dimensione Tab: '
String newFileMsg,      'Nome del File: '
String gotoMsg          'Salta alla linea: '
String editingMsg       '-- INSERISCI --  ' 
String editingMsg2      '-- SOVRASCRIVI --  ' 
String cmdMsg		'-- COMANDI --'
String noCmdMsg		'Comando inesistente. Premi un tasto per continuare.'
String cmdPrompt	':'
String findMsg          'Trova: '
String replaceMsg       'Sostituisci con: '
String notFoundMsg      'Nessuna altra stringa trovata.'
String anyKeyMsg        'Premi un tasto per continuare.'
String modifiedMSg      "Il file e' stato modificato, Usa Alt+K per uscire senza salvare"
String cancelledMsg     'Annullato.'
BAK                     db  '.BAK', 0


String stringaComandi	' qefhwj!'

helpMsg db 'Avim - un clone di vi(m) scritto in assembler -- Matteo Merli . Matteo Lucchini '
        db '--------------------------------------------------------------------------------'
        db 'MOVIMENTI CURSORE                                                               '
        db '  A sinistra (destra) di una parola  Ctrl-freccia sinistra (destra)             '
        db '  Inizio,fine linea   Home, End                                                 '
        db "  Pagina su           PgUp                  Pagina giu'         PgDn            "
        db '  Inizio file         Ctrl-Home             Fine file           Ctrl-End        '
        db '  Salta alla linea    Alt-J   (:j)                                              '
        db 'TROVA     Alt-F * (:f)          --          Sostituisci         Alt-R *         '                  
        db 'SALVA il file          Alt-W    (:w)                                            '
        db '      Esci             Alt-Q    (:q)                                            '
        db '      Non salvare le modifiche     Alt-K     (:!)                               '
        db '      Apri un altro file        Alt-E *  (:e)                                   '
        db 'CANCELLA parola a sinistra Ctrl-O      cancella parola a destra Ctrl-P          '
        db '     Cancella linea      Ctrl-Y                                                 '
        db 'SETTA margini S,D    Ctrl-PgUp, Ctrl-PgDn                                       '
        db 'Imposta la lunghezza del tab        Alt-T                                       '
        db 'Cambia i colori dello schermo       Alt-C                                       '    
        db "Modalita' autoindent                Alt-A                                       "
        db "PASSA da modalita' inserimento a modalita' comandi e viceversa        Esc       "
        db 'Inserisci caratteri di controllo come testo normale      Ctrl-J                 '
        db '--------------------------------------------------------------------------------'
        db "Tra  parentesi sono indicati i comandi da utilizzare in modalita' comandi.      "
        db "* = usa stringa precedente se e' premuto il tasto Shift.                        "



;Variabili

newFile?        db 0            ;Vero se e' un nuovo file
marking?        db 0            ;Vero se il testo e' marcato
changed?        db 0            ;Vero se il file originale e' stato cambiato
isBAKed?        db 0            ;Vero se il file .BAK e' gie' stato creato
needCopies?     db -1           ;Vero a meno che le ultime linee del buffer siano state cancellate
autoReplace?    db 0            ;-1 se auto-replace con shift, 1 senza shift
noEscape?       db 0            ;Vero se il prompt richiede response

;Queste variabili e buffer hanno memoria allocata alla fine del codice


B? attribNl, 1                  ;Attributi e stato del testo
B? attribInv, 1
W? cursorShape, 1               ;Parametri del cursore per colore e mono
B? fName?, 1                    ;Vero se il nome del file e' quello sulla linea dei comandi
B? justFound?, 1                ;Vero se non ci sono stati altri comandi
W? lMargin, 1                   ;Margini correnti
W? rMargin, 1
W? fHandle, 1                   ;Handle del file
W? lastLine, 1                  ;Indici dell'ultima linea del file
W? blockPtrsLast, 1             ;Indice dell'ultima linea nel blocco buffer
W? top, 1                       ;Indice della prima linea su schermo
W? bottom, 1                    ;Indice dell'ultima linea su schermo
W? mark,1                       ;
W? here, 1                      ;Temporaneo
W? bufferPtr, 1                 ;Puntatori al buffer multifunzione
W? hereCol, 1
W? topSegPtr, 1
W? topSeg, 1
W? topIndex, 1
W? videoSegment, 1              ;Segmento della memoria video del sistema
W? heapStart, 1                 ;Segmento di inizio dell'heap
W? heapPtr, 1                   ;Puntatore a segmento al prossimo paragrafo nell'heap
B? fName, 50                    ;Nome del file in formato ASCIIZ
newfName db 'nuovo.txt',00h
B? otherFName, 50               ;Nome del secondo file
W? otherLine, 1                 ;Linea del cursore nel secondo file
W? temp, 1                      ;Strorage Temporaneo
W? stackPointer, 1		;Puntatore al primo valore dello stack (per resettarlo) 
B? fNameBAK, LINEWIDTH          ;File corrente con estensione .BAK
B? fNameTemp, LINEWIDTH         ;Nome del file per vari scopi
B? pad, LINEWIDTH               ;Buffer di note
B? findString, LINEWIDTH        ;Stringa da cercare con TROVA
B? Comando, 10			;Comandi da impartire al programma
B? replaceString, LINEWIDTH     ;Nuova stringa per il comando Replace
W? linePtrs, MAXLINES           ;Lista dei puntatori a linea
W? blockPtrs, MAXLINES          ;Puntatore a linea per le linee cancellate
B? buffer, BUFFERLENGTH         ;Buffer del file


;Tabella dei salti:   ^ = Ctrl, @ = Alt, # = Shift.
ctrlTable       dw na           ;Non definito
	dw	WordLeft     ;^A
	dw	na           ;^B
	dw	PageDown     ;^C
	dw	Right        ;^D
	dw	Up           ;^E
	dw	WordRight    ;^F
	dw	na           ;^G o BEL
	dw	BackSpace    ;^H o BS
	dw	Tab          ;^I o HT
	dw	InsertRaw    ;^J o LF
	dw	na           ;^K o VT
	dw	na           ;^L o FF
	dw	CRet         ;^M o CR
	dw	na           ;^N o SO
	dw	DeleteWordL  ;^O o SI
	dw	DeleteWordR  ;^P
	dw	na           ;^Q o DC1
	dw	PageUp       ;^R o DC2
	dw	Left         ;^S o DC3
	dw	na           ;^T o DC4
	dw	na           ;^U
	dw	na           ;^V
	dw	na           ;^W
	dw	Down         ;^X o CAN
	dw	DeleteLine   ;^Y
	dw	na           ;^Z
	dw	na           ;^[
	dw	DeleteToEOL  ;^\
	dw	na           ;^]
	dw	na           ;^^
	dw	na           ;^-

auxTable        dw 3 DUP (na)   ;Non definita
	dw	na           ;^@ o NUL
	dw	11 DUP (na)  ;Non definita
	dw	ReverseTab   ;#Tab
	dw	Exit         ;@Q
	dw	Save         ;@W
	dw	OtherFile    ;@E
	dw	Replace      ;@R
	dw	SetTabs      ;@T
	dw	na           ;@Y
	dw	na           ;@U
	dw	na           ;@I
	dw	na           ;@O
	dw	na           ;@P
	dw	4 DUP (na)   ;Non definita
	dw	AutoIndent   ;@A
	dw	na           ;@S
	dw	na           ;@D
	dw	Find         ;@F
	dw	na           ;@G
	dw	Help         ;@H
	dw	Jump         ;@J
	dw	Kill         ;@K
	dw	na           ;@L
	dw	5 DUP (na)   ;Non definita
	dw	na           ;@Z
	dw	na           ;@X
	dw	setColor     ;@C
	dw	na           ;@V
	dw	na           ;@B
	dw	na           ;@N
	dw	na           ;@M
	dw	8 DUP (na)   ;Non definita
	dw	Help         ;F1
	dw	na           ;F2
	dw	na           ;F3
	dw	na           ;F4
	dw	na           ;F5
	dw	na           ;F6
	dw	na           ;F7
	dw	na           ;F8
	dw	na           ;F9
	dw	na           ;F10
	dw	2 DUP (na)   ;Non definita
	dw	HomeLine     ;Home
	dw	Up           ;Up arrow
	dw	PageUp       ;PgUp
	dw	na           ;Non definita
	dw	Left         ;Left arrow
	dw	na           ;Non definita
	dw	Right        ;Right arrow
	dw	na           ;Non definita
	dw	EndLine      ;End
	dw	Down         ;Down arrow
	dw	PageDown     ;PgDn
	dw	ToggleIns    ;Ins
	dw	Delete       ;Del
	dw	na           ;#F1
	dw	na           ;#F2
	dw	na           ;#F3
	dw	na           ;#F4
	dw	na           ;#F5
	dw	na           ;#F6
	dw	na           ;#F7
	dw	na           ;#F8
	dw	na           ;#F9
	dw	na           ;#F10
	dw	20 DUP (na)  ;[^Fn, @Fn]
	dw	na           ;^PrtSc
	dw	WordLeft     ;^Left arrow
	dw	WordRight    ;^Right arrow
	dw	BottomFile   ;^End
	dw	SetRMargin   ;^PgDn
	dw	TopFile      ;^Home
	dw	10 DUP (na)  ;[Alt numero]
	dw	na           ;@-
        dw      na           ;@=
	dw	SetLMargin   ;^PgUp

;******************************************************************************

Start:
	mov	ax, cs
	mov	ds, ax
	mov	es, ax

	call	malloc
	
	mov	cx, [colorAttributes]     ;Uso gli attributi di colore di default
	
	mov	[videoSegment], 0b800h
	mov	[attribNl], ch
	mov	[attribInv],cl
	mov	[cursorShape], 0607h
	
	mov	di, OFFSET fName
	mov	si, OFFSET newfName
	mov	cx, 10
	rep	movsb

InitFile:
	mov	dx, OFFSET fName          ;Apro il file e inizio la lista di puntatori a linea
	cmp	[fName?], 0               ;Se nessun file e' specificato nella riga comandi
	jne	@@L0
	mov	[noEscape?], -1           ;Promptalo
	call	GetFileName
@@L0:
	call	OpenFile
	mov	[lMargin], 0              ;Setto i margini iniziali
	mov	[rMargin], LINEWIDTH - 1
	jmp	NextKey

NextKey:
	call	Redraw                   ;Ridisegna lo schermo
NextNoRedraw:
	call	DrawCursor               ;Piazza il cursore
	sub	ah, ah                    ;Mette il tasto premuto in al
	int	16h

	cmp	al,ESCAPE
	jne	@@SaltaESc
	call	CmdMode
	jmp	NextKey
@@SaltaEsc:
	or	al, al                     ;Controlla per codici di comando
	je	IsAux
	cmp	al, ' '
	jb	IsCtrl
	call	Insert                   ;Insert o sovrascrivi se non ci sono
	jmp	NextKey

IsAux:
	xchg	al, ah                   ;Ottieni il codice Aux
	cmp	al, 132
	ja	NextKey
	mov	si, OFFSET auxTable       ;Salto indiretto alla routine appropriata
DoTableJump:
	shl	ax, 1
	add	si, ax
	call	[WORD si]
	jmp	NextKey

IsCtrl:
	mov	si, OFFSET ctrlTable      ;Salta alla routine attraverso la tabella
	sub	ah, ah
	jmp	DoTableJump

InsertRaw:
	call	Shifted?                 ;Setta il flag se e' premuto shift
	mov	dl, al
	sub	ah, ah                    ;Ottieni il tasto premuto in al
	int	16h
	or	al, al                     ;Controlla se e' un codice Aux ignorando Insertraw
	je	IsAux
	cmp	dl, 0                     ;Se lo shift era premuto ottieni il bit alto
	jz	@@L1
	or	al, 80h
@@L1:
	call	Insert                   ;Inserisci il carattere
	jmp	NextKey


;-------------------------------------------
; Command Mode

CmdMode:
	not	[commandMode?]
	
@@loop0:
	call	Redraw

	sub	ah, ah                    ;Ottieni il tasto premuto in al
	int	16h

	cmp	al,ESCAPE
	jne	@@SaltaESc
	jmp	@@End_                    ;Ritorna alla modalita' inserimento
@@SaltaEsc:
	or	al, al                     ;Controlla per codici di comando
	je	@@IsAux
	cmp	al, ' '
	jb	@@IsCtrl
	cmp	al,':'
	je	@@LeggiComando                    
	jmp	@@loop0

@@IsAux:
	xchg	al, ah
	cmp	al, 132
	ja	@@loop0
	mov	si, OFFSET auxTable
@@DoTableJump:
	shl	ax, 1
	add	si, ax
	call	[WORD si]
	jmp	@@loop0
@@IsCtrl:
	mov	si, OFFSET ctrlTable
	sub	ah, ah
	jmp	@@DoTableJump

;---------------------------------------------------------------------------
;LeggiComando mostra il prompt e legge un comando
;dopodiche cerca la stringa immessa all'interno della stringa 
;che contiene tutti i comandi.
;Alla fine, dal valore di CX si potra' sapere qual era il comando immesso
@@LeggiComando:

	push	dx

	mov	si, OFFSET cmdPrompt
	mov	dx, OFFSET Comando
	call	GetCountedString 	; Stampa il prompt ':' e legge il comando
	mov	si,OFFSET Comando
	lodsb
	lodsb
	mov	dl,al

	xor	cx,cx
	mov	si, OFFSET stringaComandi
	lodsb
	mov	cl,al   			; Metto in cx il numero di caratteri del comando
	mov	ch,al			; e lo salvo anche in ch

@@loop1:  
	lodsb
	cmp	dl,[si]
	je	@@trovato
	dec	cl
	jnz	@@loop1		; Cerca il carattere nella stringa dei comandi

@@nonTrovato:
	mov	si, OFFSET noCmdMsg
	call	Print0
	call	Beep
	xor	ah, ah
	int	16h    	;Attende che venga premuto un tasto
	jmp	@@L1

@@trovato:
	mov	al,ch
	sub	al,cl
	cmp	al,0
	jne	@@next0
	call	exit  ; q -> Esci
@@next0:
	cmp	al,1
	jne	@@next1
	call	OtherFile  ; e -> Apri file
@@next1:
	cmp	al,2
	jne	@@next2
	call	Find  ; f -> Trova
@@next2:
	cmp	al,3
	jne	@@next3
	call	Help  ; h -> Help
@@next3:
	cmp	al,4
	jne	@@next4
	call	Save  ; w -> Salva il file se e' stato modificato
@@next4:
	cmp	al,5
	jne	@@next5
	call	Jump  ; j -> Salta alla linea
@@next5:
	cmp	al,6
	jne	@@next6
	call	Kill  ; ! -> Marca il file come non modificato e viceversa
@@next6:

@@L1:
	pop	dx
	jmp	@@loop0  ;Torna all'inizio della modalità comandi

	
@@End_:
	not	[commandMode?]
	ret

;--------------------------------------------------------------------------------------

;Scambia il vecchio ed il nuovo nome del file prima di aprire un nuovo file
SwapNames:
	mov	si, OFFSET fName
	mov	di, OFFSET otherFName
	mov	cx, 50
@@L1:
	mov	al, [si]
	xchg	al, [di]
	mov	[si], al
	inc	si
	inc	di
	loop	@@L1
	ret

;Apre un'altro file (dopo aver chiuso quello aperto)
OtherFile:
	call	Shifted?                 ;Setta il flag di Shift
	mov	dh, [byte OtherFName]     ;Ignora, se non e' il file principale
	or	dh, dh
	jz	@@L1
	mov	dh, al
@@L1:
	call	Save                     ;Salva il file corrente se e' stato modificato
	mov	ax, [blockPtrsLast]       ;Se il buffer e' vuoto,
	sub	ax, OFFSET blockPtrs
	jne	@@L2
	mov	ax, [heapStart]           ; risetto il puntatore all'heap all'inizio
	mov	[heapPtr], ax
	jmp	SHORT GetOther            ; Richiede il nuovo nome del file
@@L2:
	shr	ax, 1                     ;Altrimenti muovo i puntatori nel buffer
	mov	cx, ax                    ;all'inizio dell'heap
	mov	dl, 5
	mul	dl
	add	ax, [heapStart]           ;Calcola il limite superiore della zona bersaglio
	mov	[heapPtr], ax             ;che sara' anche il nuovo valore dell'heap pointer
	mov	bx, OFFSET blockPtrs      ;Per ogni puntatore nel buffer,
@@L3:
	cmp	[bx], ax                  ;Se la sua linea e gia' dentro alla zona,
	jae	@@L4
	mov	es, [bx]                  ;Setla il bit alro del primo char per segnalarlo
	or	[byte es:0], 80h           ;non ci sara' bisogno di muovere queste linee
@@L4:
	inc	bx                        ;Prossimo puntatore
	inc	bx
	loop	@@L3
	push	ds
	mov	bx, OFFSET blockPtrs      ;Per ogni puntatore nel buffer:
	mov	es, [heapStart]
@@L4a:
	test	[byte es:0], 80h         ;Se il bit alto e' segnato nella line corrente,
	jne	@@L7                      ;la linea e' gia' in uso,provo la prossima
	mov	ds, [cs:bx]               ;Se il bit alto e' segnato nella linea sorgente
	test	[byte 0], 80h
	je	@@L5                       ; non devo muoverla (c'e' gia')
	inc	bx                        ; Prossima linea sorgente, stessa linea bersaglio
	inc	bx
	jmp	SHORT @@L8
@@L5:
	mov	cx, 40                    ;Altrimenti muovi una linea
	sub	si, si
	sub	di, di
	rep	movsw
	mov	[cs:bx], es               ;Aggiorna il puntatore
@@L6:
	inc	bx                        ;prossimo puntatore
	inc	bx
@@L7:
	mov	ax, es                    ;Prossima linea bersaglio
	add	ax, 5
	mov	es, ax
@@L8:
	segcs	;Loop finche' tutte le linee sono state mosse
	cmp	bx, [blockPtrsLast]
	jb	@@L4a
	pop	ds
	mov	bx, OFFSET blockPtrs      ;Risetta tutti i primi bit alti cambiati
@@L9:
	mov	es, [bx]
	and	[byte es:0], 7Fh
	inc	bx
	inc	bx
	cmp	bx, [blockPtrsLast]
	jb	@@L9
GetOther:
	call	SwapNames
	cmp	dh, 0                     ;Se Shifted, usa l'ultimo file
	je	@@L1
	mov	dx, OFFSET fName
	call	OpenFile1
	mov	bx, [otherLine]           ;Riporta il cursore alla posizione precedente
	mov	ax, [here]
	mov	[otherLine], ax
	jmp	NewLine
@@L1:
	mov	[noEscape?], -1
	mov	ax, [here]                ;Salva il nome del file corrente e la posizione del cursore
	mov	[otherLine], ax
	mov	dx, OFFSET fName          ;Prompt per un nuovo file
	call	GetFileName
	jmp	SHORT OpenFile1           ;Legge il nuovo file


;Apre file, lo carica se trovato,poi chiude. Richiede il nome ASCIIZ in dx
OpenFile:
	mov	ax, OFFSET blockPtrs      ;Risetta il puntatore nel buffer
	mov	[blockPtrsLast], ax
	mov	ax, [heapStart]           ;Inizia a caricare dalla cima dell'heap
	mov	[heapPtr], ax
OpenFile1:
	mov	[newFile?], 0
	mov	[changed?], 0
	mov	ax, 3D00h                 ;Prova ad aprire il file
	int	21h
	jnc	OldFile
	mov	[newFile?], -1            ;Se non esiste,ricordami di crearlo
OpenNewFile:
	call	NewFile
	jmp	SHORT XOpenFile
OldFile:
	mov	[fHandle], ax             ;Altrimenti salva l'handle
	mov	bx, OFFSET linePtrs       ;e leggi il file
	mov	dx, ax
	call	ReadFile
	dec	bx
	dec	bx
	mov	[lastLine], bx            ;Salva l'indice dell'ultima linea
	mov	bx, [fHandle]             ;Chiude il file
	mov	ah, 3Eh
	int	21
XOpenFile:
	mov	bx, OFFSET linePtrs       ;Ripristina riga e posizione su schermo
	mov	[top], bx
	mov	es, [bx]
	sub	di, di
	ret

GetFileName:
;Cerca il file nominato.Controlla che non sia nullo. Richiede in dx l'indirizzo
; del buffer
	push	si
	push	dx
	mov	si, OFFSET newFileMsg     ;Stampa il prompt
	call	Prompt
	pop	dx
	call	GetString                ;Memorizzo il nome del file
	mov	si, dx                    ;e lo converto in ASCIIZ
	add	si, ax
	mov	[BYTE si], 0
	pop	si
	ret

GetString:
;Prende la stringa in [dx] e ritorna la lunghezza (meno CR\LF). Esce se la
; stringa e' vuota
	push	bx
	push	cx
	push	si
	push	di
	push	es
	push	dx
@@L2:
	mov	dx, OFFSET pad            ;Memorizzo la stringa
	mov	ah, 3Fh
	sub	bx, bx
	mov	cx, 20
	int	21h
	dec	ax                        ;Tolgo CR/LF
	dec	ax
	jnz	@@L1                      ;Esco se la stringa e' vuota
	cmp	[noEscape?], 0            ;a meno che non sia bloccata l'uscita
	je	@@L0
	call	Beep
	mov	si, OFFSET newFileMsg
	call	Prompt
	jmp	@@L2
@@L0:
	mov	si, OFFSET cancelledMsg
	jmp	Abort
@@L1:
	mov	cx, ax                    ;Metto la copia temporanea in [DX]
	pop	dx
	push	ax
	mov	ax, ds
	mov	es, ax
	mov	si, OFFSET pad
	mov	di, dx
	rep	movsb
	pop	ax
	pop	es
	pop	di
	pop	si
	pop	cx
	pop	bx
	mov	[noEscape?], 0 
	ret

	

;Carico il file con il descriptor in DX, ponendo l'inizio del puntatore a segmento in BX
ReadFile:
	push	es
	mov	ax, [heapPtr]
	mov	es, ax
	sub	cx, cx
	sub	di, di
FillBuffer:
	push	bx                       ;Riempio il buffer
	push	cx
	push	dx
	mov	bx, dx
	mov	ah, 3Fh
	mov	cx, BUFFERLENGTH
	mov	dx, OFFSET buffer
	int	21h
	jnc	@@L1                      ;Controllo eventuali errori di lettura
	jmp	ReadError
@@L1:
	pop	dx
	pop	cx
	pop	bx
	mov	si, OFFSET buffer         ;Setto i puntatori
	add	ax, si
	mov	[bufferPtr], ax
	cmp	ax, OFFSET buffer         ;Esco se il buffer e' vuoto
	je	EndOfFile
	cmp	[byte si], LF             ;Salto Lf se e' il primo carattere nel buffer
	jne	SHORT NextLine
	inc	si
NextLine:
	mov	al, [si]                  ;Prendo il prossimo carattere
	cmp	al, CR                    ;Se e' CR sono a fine linea
	jne	@@L2
	inc	si                        ;mi muovo oltre CR
	cmp	[byte si], LF             ;ed LF se presente
	jne	@@L1
	inc	si
@@L1:
	call	EndOfLine                ;Stampo la linea con spazi, se presenti,e salvo
	jmp	SHORT @@L3
@@L2:
	cmp	al, HT                    ;Se e' un tab, lo espando
	jne	@@L2a
	push	cx
	mov	al, ' '
	mov	cl, [tabSize]
	sub	ch, ch
	rep	stosb
	pop	cx
	sub	cl, [tabSize]
	sbb	ch, 0
	inc	si
	jmp	SHORT @@L3
@@L2a:
	movsb	;Altrimenti aggiungo il carattere
	dec	cx
@@L3:
	cmp	si, [bufferPtr]           ;Ciclo fino alla fine del buffer
	jb	NextLine
	cmp	si, OFFSET buffer + BUFFERLENGTH  ;Se il buffer e' vuoto,il file e' finito
	jae	FillBuffer
EndOfFile:
	call	EndOfLine                ;Finisco con la linea attuale
	mov	[heapPtr], es             ;Aggiorno il puntatore alla cima libera dell'heap
	pop	es
	ret

EndOfLine:
	add	cx, LINEWIDTH             ;Stampo fino alla fine con spazi
	jle	@@L1                      ;Tronco le linee troppo lunghe (LINEWIDTH)
	mov	al, ' '
	rep	stosb
@@L1:
	mov	[bx], es                  ;Immagazzino il segmento della linea corrente
	mov	ax, es                    ;Prossima linea
	add	ax, 5
	cmp	ax, 0A000h                ;Sono a corto di spazio?
	jb	SHORT @@L2
	jmp	NoRoom
@@L2:
	mov	es, ax
	inc	bx
	inc	bx
	sub	di, di
	sub	cx, cx
Ret3:
	ret

;Aggiorna la barra di stato e tutta la schermata
Redraw:
	mov	[here], bx
	mov	[hereCol], di
	push	bx
	push	di
	push	ds
	push	es
	push	di


	mov	es, [videoSegment]        
	mov	di,0f00h
	mov	cx,LINEWIDTH
	mov	ah,[attribInv]
	mov	al,' '
	rep	stosw

	cmp	[commandMode?],-1
	je	@@S00
	mov	si, OFFSET cmdMsg       ; -- COMANDI --
	jmp	@@S01
@@S00:
	test	[Inserting?], -1
	je	@@S0
	mov	si, OFFSET editingMsg     ; -- INSERISCI --
	jmp	@@S01
@@S0:
	mov	si, OFFSET editingMsg2    ; -- SOVRASCRIVI --
@@S01:
	call	Prompt 
	mov	di, 0f00h + 60            ;Scrive il nome del file a partire dalla colonna 40
	mov	si, OFFSET fName          ; <file name>
	mov	ah, [attribInv]
@@S1:
	lodsb
	or	al, al
	je	@@S2
	stosw
	jmp	@@S1
@@S2:
	add	di, 6                     ;3 spazi
	mov	al, 'L'                   ;"L" <linea #>
	stosw
	inc	di
	inc	di
	mov	ax, bx
	sub	ax, OFFSET linePtrs
	shr	ax, 1
	inc	ax
	call	PrintInt
	add	di, 4                     ;2 spaces
	mov	al, 'C'                   ;"C" <numero colonna>
	mov	ah, [attribInv]
	stosw
	inc	di
	inc	di
	pop	ax                        ;Copio DI riga del cursore
	inc	ax
	call	PrintInt
	mov	di, 0f00h + LINEWIDTH * 2 - 12    ;Mi porto all'inizio dei caratteri di stato
	mov	al, 'I'                   ;Modalita' Insert/Sovrascrivi
	mov	ah, [attribInv]
	test	[inserting?], -1
	jne	@@S3
	mov	al, 'O'
@@S3:
	stosw
	mov	al, 'M'                   ;Scrive M se il file e' stato modificato
	test	[changed?], -1
	jne	@@S5
	mov	al, ' '
@@S5:
	stosw
	mov	al, 'A'                   ;Stato dell'autoindent
	test	[autoIndent?], -1
	jne	@@S6
	mov	al, ' '
@@S6:
	stosw
	mov	al, ' '                   ;E' settato il margine sinistro?
	cmp	[lMargin], 0
	je	@@S7
	mov	al, '['
@@S7:
	stosw
	mov	al, ' '                   ;e quello destro?
	cmp	[rMargin], LINEWIDTH - 1
	je	@@S8
	mov	al, ']'
	stosw
@@S8:
	segcs
	mov	al, [attribNl]            ;Segna i margini come caratteri non invertiti
	mov	di, [lMargin]
	add	di, di
	je	@@S8a
	inc	di
	stosb
@@S8a:
	mov	di, [rMargin]
	cmp	di, LINEWIDTH - 1
	je	@@S8b
	add	di, di
	inc	di
	stosb
@@S8b:
	mov	di, 00h 		        ;Sposto il cursore all'inizio dello schermo
	mov	ax, [top]                 ;Processa il fondo dello schermo
	mov	bx, ax
	add	ax, (SCREENLENGTH - 1) * 2
	cmp	ax, [lastLine]            ;Se sono alla fine del file,
	jle	@@L0
	mov	ax, [lastLine]            ;mi fermo all'ultima linea
@@L0:
	mov	[bottom], ax
@@L1:                           ;Per ogni riga
	mov	cx, LINEWIDTH             ;Conto i caratteri per riga
	mov	ds, [cs:bx]               ;Prendo il puntatore alla linea su schermo
	sub	si, si                    ;Inizializzo il contatore delle colonne
	segcs
	mov	ah, [attribNl]            
@@L2:                           ;Per ogni carattere lo scrivo con l'attributo
	lodsb
	stosw
	loop	@@L2                     ;Prossimo carattere
	inc	bx                        ;Prossima riga
	inc	bx
	segcs
	cmp	bx, [bottom]              ;Mi fermo se lo schermo e' pieno
	jle	@@L1
	mov	cx, LINEWIDTH * 2 * (SCREENLENGTH )  ;Pulisce lo schermo dove non c'e' testo
	sub	cx, di
	mov	bx, di                              ;Salvalo per dopo
	shr	cx, 1
	segcs
	mov	ah, [attribNl]
	mov	al, ''
	rep	stosw

	mov	di, bx
	and	ah, 0f0h
	or	ah, 04h
	mov	al, 126
@@L2b:
	cmp	di, 0eF0h
	jae	@@L3
	stosw	; Riempie lo schermo con i tilde '~' a inizio riga
	add	di, (LINEWIDTH-1) *2
	jmp	@@L2b
	

@@L3:
	pop	es
	pop	ds
	pop	di
	pop	bx
	ret

;Imposta la dimensione del cursore
DrawCursor:
	push	bx
	mov	cx, [cursorShape]         ;Il cursure e' una linea in modalita' "Inserisci",
	test	[Inserting?], -1
	jne	@@L1
	sub	ch, ch                    ;un blocco in "Sovrascrivi"
@@L1:
	mov	ah, 1
	int	10h
	sub	bx, [top]                 ;Mostra il cursore alla posizione corrente
	shr	bx, 1
	mov	dh, bl
	mov	ax, di
	mov	dl, al
	mov	ah, 2
	mov	bh, 0
	int	10h
	pop	bx
	ret

Print0:
	call	ClearStatus              ;Pulisce la barra di stato
	mov	di,0f00h                 ;Scrive la stringa all'inizio dell'ultima riga

;Stampa la stringa puntata da SI sulla barra di stato partendo da DI
Print:
	push	es
	mov	es, [videoSegment]
	lodsb	;Ottengo la lunghezza della stringa da stampare
	mov	cl, al
	sub	ch, ch
	mov	ah, [attribInv]           
@@L1:
	lodsb
	stosw
	loop	@@L1
	pop	es
	ret

;Pulisce la barra di stato
ClearStatus:
	push	di
	push	es
	mov	es, [videoSegment]
	mov	ah, [attribInv]
	mov	al, ' '
	mov	cx, 80
	mov	di, 0f00h
	rep	stosw
	pop	es
	pop	di
	ret

;Stampa la stringa contata puntata da SI sulla linea di stato
Abort:
	call	Print0                   ;Stampa messaggio di errore

;Tasto non assegnato o altro errore. Suona e torna nel ciclo principale
na:
	call	Beep                     ;Beep
	mov	sp, [stackPointer];OFFSET STACKTOP       ;Riavvio lo stack pointer alla cima
	mov	bx, [here]                ;Recupera la posizione del cursore
	mov	di, [hereCol]
	call	DrawCursor
	jmp	NextNoRedraw              ;Ricomincia il ciclo principale di stampa

Beep:
	mov	ah, 2                     ;Suona
	mov	dl, BELL
	int	21h
@@freeBuffer:
	mov     ah,01h			; Cancella i caratteri presenti
	int     016h 			; nel buffer di lettura
	jz      @@bufferFreed
	mov     ah,00h
	int     016h
	jmp     @@freeBuffer
@@bufferFreed:

	ret

Prompt:
	push	di
	call	Print0                   ;Stampa la stringa a inizio linea
	mov	dx, di                    ;Setta il cursore alla fine della stampa
	shr	dl, 1
	mov	dh, 24     ; In fondo allo schermo
	pop	di

;Posizione del cursore alla riga,colonna forniti da DH,DL
GotoXY:
	push	bx
	push	dx
	mov	ah, 2
	sub	bx, bx
	;sub	dh ,1
	int	10h
	pop	dx
	pop	bx
	ret

;Stampa in ES:DI il numero decimale naturale contenuto in AX
PrintInt:
	sub	dx, dx                    ;Inizia lo stack con un nullo
	push	dx
	mov	cx, 10                    ;Ottiene il resto delle divisioni per dieci
@@L1:
	div	cx
	add	dl, '0'                   ;Converte in ASCII
	mov	dh, [attribInv]           
	push	dx
	sub	dx, dx
	or	ax, ax
	jne	@@L1
	pop	ax                        ;Ottieni e stampa il resto
@@L2:
	stosw
	pop	ax
	or	ax, ax
	jne	@@L2
	ret

;Inserisce una nuova riga
NewLineC:
	mov	[changed?], -1
NewLine:
	mov	[justFound?], 0
NewLine0:
	cmp	bx, OFFSET linePtrs       ;Controllo i limiti, li cambio se necessario
	jge	@@L1
	mov	bx, OFFSET linePtrs
@@L1:
	cmp	bx, [lastLine]
	jle	@@L2
	mov	bx, [lastLine]
@@L2:
	mov	ax, [top]
	cmp	bx, ax
	jge	@@L3
	mov	[top], bx
@@L3:
	add	ax, (SCREENLENGTH-1)*2
	cmp	bx, ax
	jle	@@L4
	mov	ax, bx
	sub	ax, (SCREENLENGTH-1)*2
	mov	[top], ax
@@L4:
	mov	es, [bx]                  ;Aggiusto ES per puntare alla nuova linea
	ret

Left:
	or	di, di                     ;Se a inizio linea,
	jne	@@L1
	call	Up                       ; sali di una linea
	jmp	EndLine
@@L1:
	dec	di                        ; altrimenti decrementa il cursore
CursorMoved:
	mov	[justFound?], 0
	ret

Right:
	cmp	di, LINEWIDTH - 1         ;Se a fine linea,
	jne	@@L1
	sub	di, di                    ; muoviti all'inizio di quella seguente
	jmp	Down
@@L1:
	inc	di                        ; altrimenti incrementa il cursore
	jmp	SHORT CursorMoved

;Cerca il primo carattere != ' ' a sinistra della stringa corrente
LScanE:
	mov	al, ' '
	mov	cx, di
	inc	cx
	std
	repe	scasb
	cld
	ret

;Cerca il primo spazio a sinistra della stringa corrente
LScanNE:
	mov	al, ' '
	mov	cx, di
	inc	cx
	std
	repne	scasb
	cld
	ret

;Cerca il primo carattere != ' ' a destra della stringa corrente
RScanE:
	mov	al, ' '
	mov	cx, LINEWIDTH
	sub	cx, di
	repe	scasb
	ret

;Cerca il primo spazio a sinistra della stringa corrente
RScanNE:
	mov	al, ' '
	mov	cx, LINEWIDTH
	sub	cx, di
	repne	scasb
	ret

;Sposta il cursore a sinistra di una parola
WordLeft:
	or	di, di                     ;Se siamo a inizio linea, non fare niente
	je	@@Lx
	mov	[justFound?], 0
	dec	di                        
	call	LScanE                   ; salta gli spazi
	inc	di
	je	@@Lx                       
	call	LScanNE                  
	jne	@@L1
	inc	di
@@L1:
	inc	di
@@Lx:
	ret

;Sposta il cursore a sinistra di una parola
WordRight:
	cmp	di, LINEWIDTH - 1         ;Se siamo a fine linea, non fare niente
	je	@@Lx
	mov	[justFound?], 0
	call	RScanNE                  ; salta gli spazi
	jne	@@L1                     
	dec	di
	call	RScanE                  
@@L1:
	dec	di
@@Lx:
	ret

;Muove il cursore al margine sinistro
HomeLine:
	mov	di, [lMargin]
	mov	[justFound?], 0
	ret

;Muove il cursore alla fine del testo
EndLine:
	push	cx
	mov	[justFound?], 0
	mov	di, [rMargin]             ;Muovi al margine destro
	cmp	[BYTE es:di], ' '         ;Se c'e' un carattere, e' questo il fine linea
	jne	@@L2
	call	LScanE                   ;Salta tutti gli spazi
	je	@@L1                       ; fino all'inizio della linea
	inc	di
@@L1:
	inc	di
@@L2:
	cmp	di, [lMargin]             
	jae	@@L3
	mov	di, [lMargin]
@@L3:
	pop	cx
Ret2:
	ret

;Muove il cursore su di una linea
Up:
	cmp	bx, OFFSET linePtrs       ;Se siamo gia' in cima al file, avvisa con il beep 
	jne	_Up
	call	Beep
	jmp	Ret2
_Up:
	dec	bx
	dec	bx
	jmp	NewLine

;Muove il cursore su di una linea
Down:
	cmp	bx, [lastLine]            ;Se siamo all'ultima linea, non fare niente
	jne	_Down
	call	Beep
	jmp	Ret2
_Down:
	inc	bx
	inc	bx
	jmp	NewLine

;Muove il cursore su di una pagina
PageUp:
	sub	bx, (SCREENLENGTH-1)*2
	jmp	NewLine

;Muove il cursore una pagina sotto
PageDown:
	add	bx, (SCREENLENGTH-1)*2
	jmp	NewLine

;Muove il cursore all'inizio del file
TopFile:
	mov	bx, OFFSET linePtrs
	mov	[top], bx
	call	HomeLine
	jmp	NewLine

;Muove il cursore alla fine del file
BottomFile:
	mov	bx, [lastLine]
	mov	es, [bx]
	mov	ax, bx
	sub	ax, (SCREENLENGTH-1)*2
	cmp	ax, OFFSET linePtrs
	ja	@@L1
	mov	ax, OFFSET linePtrs
@@L1:
	mov	[top], ax
	call	EndLine
	jmp	NewLine

;Sposta il cursore a destra fino al prossimo tabstop
Tab:
	mov	[justFound?], 0
	mov	ax, di                    
	mov	cl, [tabSize]
	div	cl
	sub	cl, ah
	sub	ch, ch
	add	di, cx
	cmp	di, LINEWIDTH             ;Se siamo oltre la fine della linea,
	jl	@@L1
	mov	di, LINEWIDTH - 1         ;imposta il cursore a fine linea
@@L1:
	ret

;Tab a sinistra
ReverseTab:
	mov	[justFound?], 0
	mov	ax, di                    ;Decrementa il puntatore DI al precedente tab stop
	dec	al
	div	[tabSize]
	mov	al, ah
	sub	ah, ah
	inc	al
	sub	di, ax
	jnc	@@L1                      ;Se siamo primo dell'inizio della linea,
	sub	di, di			;imposta il cursore a inizio linea
@@L1:
	ret

;Imposta il margine a sinistra
SetLMargin:
	cmp	[lMargin], 0
	je	@@L1
	mov	[lMargin], 0
	ret
@@L1:
	mov	[lMargin], di
	ret

;Imposta il margine a destra
SetRMargin:
	cmp	[rMargin], LINEWIDTH-1
	je	@@L1
	mov	[rMargin], LINEWIDTH-1
	ret
@@L1:
	mov	[rMargin], di
	ret

;Spezza la linea alla posizione corrente del cursore
CRet:
	push	ds
	push	es
	push	di
	push	es
	call	AddBlankLine             ;Aggiungi una nuova linea sotto di questa, ->ES:DI
	pop	ds                        ;DS:SI := posizione del cursore
	pop	si
	push	di
	mov	cx, LINEWIDTH             ;CX := n. di caratteri rimasti nella riga
	sub	cx, si
	je	@@L2
@@L1:
	movsb	;Spezza la riga,
	mov	[byte si - 1], ' '        ; e riempila di spazi fino alla fine
	loop	@@L1
@@L2:
	pop	di
	pop	es
	pop	ds
	jmp	NewLineC

;Inserisce una linea vuota sotto a quella corrente
AddBlankLine:
	mov	cx, 1                     ;Fai spazio per il nuovo puntatore in linePtr
	call	OpenRow
	inc	bx
	inc	bx
	mov	ax, [heapPtr]
	jmp	SHORT BlankLine

;Imposta la prima riga vuota di un nuovo file
NewFile:
	mov	ax, [heapStart]          
	mov	bx, OFFSET linePtrs
	mov	[lastLine], bx
BlankLine:
	mov	[bx], ax
	mov	es, ax
	add	ax, 5
	mov	[heapPtr], ax             
BlankThisLine:
	sub	di, di                    ;Pulisci la nuova linea
	mov	cx, LINEWIDTH
	mov	al, ' '
	rep	stosb
	mov	di, [lMargin]             ;Sposta il cursore all'inizio della linea
	test	[autoIndent?], -1        ;Se e' attivato l'autoIndent
	je	@@Lx
	cmp	bx, OFFSET linePtrs       ; e non siamo nella prima riga del file,
	je	@@Lx
	mov	es, [bx - 2]              ; controlla a quale colonna comincia il testo
	call	RScanE
	mov	es, [bx]
	je	@@L1                       ; a meno che la linea precedente sia vuota
	dec	di
	cmp	di, [lMargin]             
	jae	@@Lx
@@L1:
	mov	di, [lMargin]             ; nel qual caso spostati al margine sinistro
@@Lx:
	ret

;Apre CX nuove righe all'indirizzo puntato da BX
OpenRow:
	push	cx
	push	di
	push	es
	mov	ax, ds                    ;DS, ES -> segmento dati (linePtr)
	mov	es, ax
	mov	si, [lastLine]            ;SI punta all'ultima riga
	mov	di, si                    ;DI punta CX righe oltre SI
	add	di, cx
	add	di, cx
	mov	[lastLine], di            ;Aggiorna il valore di lastLine
	mov	cx, si                    ;Count = n. di righe da qui alla fine
	sub	cx, bx
	shr	cx, 1
	inc	cx
	std
	rep	movsw                     ;Muovi in alto gli elementi dell'array
	cld
	pop	es
	pop	di
	pop	cx
	ret

;Cancella il carattere alla sinistra del cursore
BackSpace:
	mov	ax, di                    ;Controlla che non sia il primo carattere del file
	add	ax, bx
	sub	ax, OFFSET linePtrs
	jz	Ret1                       ; spostati a sinistra e poi chiama Delete
	mov	[justFound?], 0
	push	di
	call	Left
	pop	ax                         
	or	ax, ax
	jne	Delete0

;Cancella il carattere alla posizione corrente del cursore
Delete:
	mov	dx, di                    ;Salva il numero della colonna
	cmp	[BYTE es:di], ' '         ;Se stiamo cancellando l'ultimo carattere della linea,
	jne	Delete0
	call	RScanE
	mov	di, dx                    ; unisci con la riga sotto
	je	Join
Delete0:
	mov	[changed?], -1
	push	di                       ; altrimenti fai slittare il testo a sinistra
	push	cx
	push	ds
	mov	cx, LINEWIDTH - 1
	sub	cx, di
	mov	si, di
	inc	si
	mov	ax, es
	mov	ds, ax
	rep	movsb
	mov	[BYTE di], ' '            ;Pulisci l'ultmo carattere della linea
	pop	ds
	pop	cx
	pop	di
Ret1:
	ret

;Unisce la linea sottostante con quella corrente
Join:
	cmp	bx, [lastLine]            ;Non fare nulla se siamo all'ultima riga del file
	je	@@Lx
	push	bx                       
	push	di
	push	ds
	push	di
	push	es
	mov	es, [bx + 2]              ;ES punta alla linea successiva
	push	es                       ;Salva ES
	mov	dx, di
	sub	di, di                    ;Rimuovi gli spazi nella linea
	call	RScanE
	dec	di
	mov	[temp], di
	call	EndLine                  ;Trova il primo carattere non-spazio a partire dalla fine
	add	dx, di                    ;Non fare nulla se la linea concatenata e' troppo lunga
	sub	dx, [temp]
	cmp	dx, LINEWIDTH
	jbe	@@L0
	pop	ax
	pop	es
	pop	di
	pop	ds
	pop	ax
	pop	ax
	jmp	Ret1
@@L0:
	mov	cx, di                    ;Count = lunghezza della riga sotto senza gli spazi alla fine
	sub	cx, [temp]
	mov	si, [temp]                ;Muovi da = inizio della riga sottostante
	pop	ds
	pop	es                        ;A = posizione corrente del cursore
	pop	di
	rep	movsb                     ;Concatena le righe
	pop	ds
	inc	bx                        ;Elimina la riga sotto
	inc	bx
	call	DeleteLineNS
	pop	di                        ;Ripristina i puntatori e ritorna
	pop	bx
@@Lx:
	jmp	NewLineC

;Alloca memoria per contenere il testo del buffer
malloc:
	mov	ah, 48h
        mov     bx, 8192                ; Alloca 128k di memoria
	jnc	@@ok
	call	Beep			;Se si verifica un errore,
	mov	si, OFFSET mallocErrMsg	;Avvisa ed esce
	call	Print
	sub	ah, ah
	int	16h
        call    exit
@@ok:   				;Altrimenti:
	mov	[HeapStart], ax		;Salva il puntatore al blocco di memoria
	ret


;-----------------------------------
;Inserisce un carattere alla posizione corrente del cursore
; o sovrascrive il carattere che c'era prima
Insert:
	mov	[justFound?], 0
	test	[inserting?], -1         ;Se stiamo inserendo il carattere, facciamo spazio
	jz	Insert1
	mov	si, [RMargin]             ;Se la linea e' piena, viene divisa
	cmp	[BYTE es:si], ' '
	je	Insert0
	push	ax
	push	bx
	push	di
	call	CRet
	pop	di
	pop	bx
	call	NewLine
	pop	ax
	jmp	SHORT Insert1
Insert0:
	push	ax
	push	cx
	push	ds
	mov	ax, es
	mov	ds, ax
	mov	si, LINEWIDTH - 1
	mov	cx, si
	sub	cx, di
	mov	di, si
	dec	si
	std
	rep	movsb
	cld
	pop	ds
	pop	cx
	pop	ax
Insert1:
	stosb	;Aggiunge il carattere
	mov	[changed?], -1
	cmp	di, [rMargin]             
	ja	WrapLine
	ret

WrapLine:
;Wrap last word on current line
	cmp	[BYTE es:di-1], ' '
	je	@@L1
	call	WordLeft
@@L1:
	call	CRet
	mov	es, [bx]
	cmp	[BYTE es:di], ' '
	jne	@@L2
	jmp	DeleteWordR
@@L2:
	jmp	EndLine

;Spezza la linea ed eventualmente ne inserisce una nuova
Wrap:
	mov	di, [rMargin]             ;Comincia dal margine destro
	cmp	bx, [lastLine]            ;Annulla se siamo alla fine del file
	je	@@Lx
@@L1:
	mov	dx, di
	call	RScanE                   ;C'e' del testo tra la posizione corrente e la fine della linea?
	cmp	di, LINEWIDTH
	mov	di, dx
	je	@@L2
	call	LScanNE                  ;Si'.  C'e' uno spazio a cui spezzare la linea?
	or	di, di
	mov	di, dx
	jle	@@Lx                      ;No, esci
	call	FindBreak                ;Si', spezza la linea, e metti il testo in quella sotto
	call	CRet
	jmp	Wrap
@@L2:
	call	EndLine                  ;Sposta il cursore a fine linea.
	mov	al, [es:di-1]             
	cmp	al, 40h
	jae	@@L2b
	cmp	al, '.'
	je	@@L2a
	cmp	al, ';'
	je	@@L2a
	cmp	al, ':'
	je	@@L2a
	cmp	al, '!'
	je	@@L2a
	cmp	al, '?'
	jne	@@L2b
@@L2a:
	inc	di
@@L2b:
	inc	di                        ;Dopo altri caratteri aggiungi uno spazio
	mov	dx, [rMargin]             ;Calcola lo spazio rimanente nella riga di sopra ->DX
	sub	dx, di
	ja	@@L3                       ;Se non c'e' piu' spazio per il testo, prossima linea
	call	Down
	call	KeepWrapping?
	jnc	@@Lx
	jmp	Wrap
@@L3:
	mov	[topSegPtr], bx           ;Salva i puntatori per questa riga
	mov	[topSeg], es
	mov	[topIndex], di
	call	Down                     ;Passa alla prossima riga
	call	KeepWrapping?            ;Il testo comincia al margine sinistro?
	jc	@@L4
@@Lx:
	jmp	NewLine                   ;No --> Finito
@@L4:
	add	di, dx                    ;Si', muovi in alto le parole della riga sotto
	call	FindBreak                 ;Trova il punto dove spezzare la linea
	mov	dx, di                    ;Imposta i puntatori ed il contatore
	mov	si, [lMargin]
	sub	bp, si
	jnz	@@L4a                     ;Prossima riga
	jmp	Wrap
@@L4a:
	mov	ax, es
	mov	di, [topIndex]
	mov	es, [topSeg]
	mov	bx, [topSegPtr]
	mov	ds, ax
	mov	cx, bp
	rep	movsb                     ;Muovi in alto la riga sotto
	mov	cx, cs
	mov	ds, cx
	cmp	dx, LINEWIDTH - 1         ;Se non e' rimasto niente nella riga sotto,
	jne	@@L5
	call	Down
	call	DeleteLine                ;Cancellala
	cmp	bx, [lastLine]            ;Se era l'ultima riga, abbiamo finito
	je	@@Lx
	mov	es, [topSeg]              
	mov	bx, [topSegPtr]
	jmp	Wrap
@@L5:
	call	Down                     ;Passa alla linea sotto
	mov	si, dx
	mov	di, [lMargin]
	call	CloseGap
	jmp	Wrap                      ;Continua a spostare la linea sotto

;Imposta DI al margine sinistro. Ritorna il carry=1 se la linea inizia al margine sinistro
KeepWrapping?:
	mov	di, [lMargin]
	cmp	[BYTE es:di], ' '         ;Fallisce se c'e' uno spazio nella pos del margine
	je	@@Lx
	or	di, di
	je	@@L1
	push	di
	dec	di                        ; o caratteri prima del margine
	call	LScanE
	pop	di
	jne	@@Lx
@@L1:
	stc	                           ; altrimenti ritorna carry = 1
	db	3Ch                        
@@Lx:
	clc
	ret

;Individua il punto in cui spezzare la linea. ritorna il valore in BP
FindBreak:
	cmp	[BYTE es:di], ' '
	je	@@L1
	inc	di
	call	WordLeft
	mov	bp, di
	ret
@@L1:
	mov	bp, di
	jmp	WordRight

;Cancella dal cursore fino alla fine della linea 
DeleteToEOL:
	mov	[justFound?], 0
	push	bx                       ;Salva i registri per ritornare alla
	push	di			;posizione corrente del cursore
	push	es
	push	[word autoIndent?]       ;Disattiva l'autoIndent
	mov	[autoIndent?], 0
	call	Cret                     ;Manda a capo il testo 
	call	DeleteWordL		;e quindi elimina la nuova linea
	call	DeleteLine
	pop	[word autoIndent?]
	pop	es
	pop	di
	pop	bx
	jmp	NewLine

;Elimina la linea corrente 
DeleteLine:
	mov	[changed?], -1
	cmp	bx, [lastLine]            ;Se si tratta dell'ultima linea del file, viene azzerata
	jne	DeleteLineNS
	jmp	BlankThisLine
DeleteLineNS:                   
	mov	di, bx                    
	mov	si, di                   
	inc	si
	inc	si
	mov	cx, [lastLine]            ;CX = numero di linee dalla pos corrente alla fine
	mov	ax, cx
	dec	ax
	dec	ax
	mov	[lastLine], ax
	sub	cx, bx
	shr	cx, 1
	mov	ax, ds                    
	mov	es, ax
	rep	movsw
	mov	di, [lMargin]             ;Muove il cursore all'inizio della nuova linea
	jmp	NewLine

;Cancella i caratteri a sinistra fino ad uno spazio (o all'inizio della riga)
DeleteWordL:
	mov	ah, 2                     ;Controlla se e' premuto Ctrl.  ^[ or ESC?
	int	16h                       
	and	al, 4
	je	DWLx
	mov	si, di                    ;Salva la posizione del cursore
	call	WordLeft                 ;Spostati di una parola a sinistra
CloseGap:
	push	di                       ;Sistema la differenza tra di e si
	mov	cx, LINEWIDTH
	sub	cx, si
	push	ds
	mov	ax, es
	mov	ds, ax
	rep	movsb
	mov	cx, LINEWIDTH             ;Riempie il resto della riga con degli spazi
	sub	cx, di
	mov	al, ' '
	rep	stosb
	pop	ds
	pop	di
	mov	[changed?], -1
DWLx:
	ret

;Elimina i caratteri a destra fino ad uno spazio
DeleteWordR:
	mov	si, di                    ;Salva il cursore
	push	di
	call	WordRight                ;Spostati di una parola a destra
	xchg	si, di                   ;Elimina la differenza tra di e si
	pop	di
	jmp	CloseGap


;Passa dalla modalita' "INSERISCI" e a quella "SOVRASCRIVI" e viceversa
ToggleIns:
	not	[inserting?]
	ret

;Salta alla linea (che viene chiesta)
Jump:
	mov	si, OFFSET gotoMsg
	call	Prompt
	call	GetInt
	dec	ax
	shl	ax, 1
	mov	bx, ax
	add	bx, OFFSET linePtrs
	mov	[justFound?], 0
	jmp	SHORT JL1        

;Legge un numero intero (in decimale) e lo salva in AX
GetInt:
	push	bx
	push	cx
	push	dx
	push	si
	mov	dx, OFFSET buffer
	call	GetString                ;Legge una stringa
	mov	cx, ax                    
	mov	si, OFFSET buffer
	sub	ax, ax
	mov	bh, 10
@@L1:
	mov	bl, [si]                  ;Legge il prox carattere
	inc	si
	sub	bl, '0'
	jc	@@Lx                       ;Esci se non è una cifra
	cmp	bl, '9'
	cmc
	jc	@@Lx
	mul	bh                        ;AX := AX + (cifra - '0')
	add	al, bl
	adc	ah, 0
	jc	@@Lx                       ;Controlla che sia andato in overflow
	loop	@@L1                     ;Prossimo carattere
@@Lx:
	pop	si                        ;Ritorna con il numero in AX
	pop	dx
	pop	cx
	pop	bx
	ret
@@La:
	mov	si, OFFSET cancelledMsg
	jmp	Abort

JL1:
	mov	ax, bx
	sub	ax, 8                     ;Sposta il cursore 5 linee sotto la prima riga dello schermo
	cmp	ax, OFFSET linePtrs
	jge	@@L1
	mov	ax, OFFSET linePtrs
@@L1:
	mov	[top], ax
JLx:
	jmp	NewLine0

;Imposta la dimensione del TAB
SetTabs:
	mov	si, OFFSET setTabsMsg
	call	Prompt
	call	GetInt
	mov	[tabSize], al
	ret

;Cicla la modalita' dei colori
SetColor:
	push	dx
	mov	al, [colorMode?]           ; Incrementa colorMode? da 0 a 2
	inc	al                         ; dopo di che la setta di nuovo a 0
	cmp	al,3
	jne	@@L1
	mov	al,0
	mov	dx, [colorAttributes]
	jmp	@@End
@@L1:
	cmp	al,1
	jne	@@L2
	mov	dx, [colorAttrib1]        ; Salva i valori degli attributi per il 
	jmp	@@End                     ; testo e per la barra
@@L2:
	mov	al,2              ;per sicurezza
	mov	dx, [colorAttrib2]
@@End:
	mov	[colorMode?], al
	mov	[attribNl], dh
	mov	[attribInv], dl
	pop	dx
	ret

;Cambia lo stato di AutoIndent
AutoIndent:
	not	[autoIndent?]
	ret

;Cambia il flag changed? che indica se il file e' stato modificato
;viene usato per uscire senza salvare
Kill:
	not	[changed?]
	ret

;Scrive il file su disco, e rinomina il vecchio file con l'estensione BAK
Save:
	cmp	[changed?], 0             ;Se il file non e' stato modificato, non fare niente
	je	XSave
	push	dx
	push	di
	push	es
	push	bx
	mov	al, [newFile?]            ;Se il file non esisteva, occorre crearlo
	or	al, [isBAKed?]             ;Se esiste già il backup, non occorre farlo
	jnz	DoSave
	mov	ax, ds                    ;altrimenti crea la stringa con il nome ASCIIZ del file bak
	mov	es, ax
	mov	si, OFFSET fName
	mov	di, OFFSET fNameBAK
@@L1:
	lodsb
	cmp	al, '.'
	je	@@L2
	or	al, al
	je	@@L2
	stosb
	jmp	SHORT @@L1
@@L2:
	mov	cx, 5
	mov	si, OFFSET BAK
	rep	movsb
	mov	ah, 41h                   ;Elimina, se è presente, il vecchio file bak
	mov	dx, OFFSET fNameBAK
	int	21h
	mov	dx, OFFSET fName          ;Rinomina il file corrente in file.BAK
	mov	di, OFFSET fNameBAK
	mov	ah, 56h
	int	21h
DoSave:
	mov	ah, 3Ch                   ;Crea il nuovo file con vecchio nome
	sub	cx, cx
	mov	dx, OFFSET fName
	int	21h
	jc	CantOpen
	mov	[fHandle], ax
	mov	[isBAKed?], -1            ;Setta il flag così la prossima volta il bak non verra' creato
	mov	bx, OFFSET linePtrs       ;Scrivi il file
	call	WriteFile
	mov	ah, 3Eh                   ;Chiudi il file
	mov	bx, [fHandle]
	int	21h
	pop	bx
	pop	es
	pop	di
	pop	dx
	mov	[changed?], 0
XSave:
	ret

;Scrive le linee del file su disco utilizzando fHandle, partendo da BX fino a lastLine
WriteFile:
	push	es
	push	di
	mov	di, OFFSET buffer
@@L1:
	mov	si, di                    ;Salva il puntatore al buffer del file
	mov	es, [bx]                  ;Elimina gli spazi all'inizio
	mov	cx, LINEWIDTH
	mov	di, LINEWIDTH - 1
	mov	al, ' '
	std
	repe	scasb
	cld
	je	@@L1a
	inc	cx
@@L1a:
	mov	ax, ds                    ;Copia la linea nel buffer del file
	mov	dx, es
	mov	es, ax
	mov	ds, dx
	mov	di, si
	sub	si, si
	rep	movsb
	mov	ax, CRLF                  ;Aggiungi CRLF alla fine
	stosw
	mov	ax, ds
	mov	dx, es
	mov	es, ax
	mov	ds, dx
	cmp	di, OFFSET Buffer + BUFFERLENGTH - 80  ;Se il buffer e' quasi pieno,
	jl	@@L2
	call	WriteBuffer              ; scrivilo
@@L2:
	inc	bx                        ;Prossima riga, continua fino a che tutte le 
	inc	bx			;righe sono state scritte
	cmp	bx, [lastLine]
	jle	@@L1
	dec	di                        ;Elimina l'ultimo CR, LF
	dec	di
	call	WriteBuffer              ;Scrive l'ultimo buffer (parzialmente riempito)
	pop	di			;nel file ed esci
	pop	es
	ret

FileError:
	mov	si, OFFSET fileErrorMsg
	jmp	Abort
CantOpen:
	mov	si, OFFSET cantOpenMsg
	jmp	Abort
NoRoom:
	mov	si, OFFSET outOfMemoryMsg
	jmp	Abort
ReadError:
	mov	si, OFFSET rdErrorMsg
	jmp	Abort
DiskFull:
	mov	dx, OFFSET fName          ;Prova a cancellare il file
	mov	ah, 41h
	int	21h
	mov	si, OFFSET diskFullMsg
	jmp	Abort

;Scrive su disco il testo nel buffer
WriteBuffer:
	push	bx
	mov	bx, [fHandle]
	mov	cx, di
	mov	dx, OFFSET Buffer
	sub	cx, dx
	jle	@@L1
	push	cx
	mov	ah, 40h
	int	21h
	jc	FileError
	pop	cx
	cmp	cx, ax
	jne	DiskFull
	mov	di, OFFSET Buffer
@@L1:
	pop	bx
	ret

Exit:
	cmp	[changed?], 0
	jz	@@Ok
	mov	si, OFFSET modifiedMsg
	call	Prompt
	call	Beep                     ;Avvisa se il file e' stato modificato, 
	mov	sp, [stackPointer]        ;Riporta stack pointer al valore iniziale
	mov	bx, [here]                ;Riporta il cursore nella sua posizione
	mov	di, [hereCol]
	call	DrawCursor
	mov	[commandMode?],-1
	jmp	NextNoRedraw 
@@Ok:
	call	RestoreCursor
	mov	ah,00H
	mov	al,03H
	int	010H    ; Pulisce lo schermo

	mov	ax,4C00H
	int	021H    ; Termina il programma

RestoreCursor:
	mov	cx, [cursorShape]         ;Ripristina il cursore normale
	mov	ah, 1
	int	10h
	mov	dx, 1800h                 ;Sposta il cursore in fondo allo schermo
	sub	bh, bh
	mov	ah, 2
	int	10h
	ret

;Controlla lo stato del tasto Shift, e lo mette in AL.  Se non e' premuto va a 0.
Shifted?:
	mov	ah, 2
	int	16h
	and	al, 3
Ret4:
	ret

;Find chiede di immettere una stringa e la cerca nel buffer a partire dalla
;posizione corrente.  Se è premuto Shift, riutilizza la stringa precendente
;(che è rimasta memorizzata in findString)
Find:
	push	bx
	push	es
	push	di
	call	Right
	test	[autoReplace?], -1       ;Se siamo in Replace all, lo stato di shift non va controllato
	js	@@L0
	jnz	@@L1
	call	Shifted?                 ;Se e' premuto Shift,
	jnz	@@L1
@@L0:
	mov	si, OFFSET findMsg        ;chiedi la stringa da cercare
	mov	dx, OFFSET findString
	call	GetCountedString
@@L1:
	mov	si, OFFSET findString
	lodsw
	dec	al
	mov	dl, al
	mov	al, ah
	mov	cx, LINEWIDTH
	sub	cx, di
FindLoop:

	push	ax
	push	cx
	mov	ax,es
	mov	cx,ds
	cmp	ax,cx
	jne	@@test1
	call	exit
@@test1:
	pop	cx
	pop	ax

	repne	scasb                   ;Esegue lo scan per il primo carattere
	jne	FindNextLine
	push	di                       ;Se viene trovato, vengono confrontati anche gli altri
	mov	dh, cl
	mov	cl, dl
	mov	si, OFFSET findString + 2
	repe	cmpsb
	je	Found			;La stringa e' stata trovata... altrimenti
	pop	di                        ;no: torna a fare lo scan per il primo carattere.
	mov	cl, dh
	jmp	FindLoop
FindNextLine:
	inc	bx                        ;Ricerca nella linea successiva (fina a EOF)
	inc	bx
	mov	es, [bx]
	sub	di, di
	mov	cl, LINEWIDTH
	cmp	bx, [lastLine]
	jbe	FindLoop
	mov	si, OFFSET notFoundMsg    ;Stringa non trovata: avverti
	test	[autoReplace?], -1      
	jz	@@L1
	mov	[autoReplace?], 0
	pop	di
	pop	es
	pop	bx
	ret
@@L1:
	call	Print0                   ;Stampa il messaggio di errore
	pop	di
	pop	es
	pop	bx
	jmp	na
Found:
	pop	di
	dec	di
	add	sp, 6
	mov	[justFound?], -1
	mov	ax, bx                    ;Sposta la linea che contiene la stringa trovata
	jmp	JL1			; 5 righe sotto la prima linea dello schermo

;Stampa la stringa puntata da SI, e legge una stringa salvando il
;testo in [DX]. Ritorna la lunghezza della stringa in AL
;Il primo carattere della stringa contiene la sua lunghezza
GetCountedString:
	push	di
	push	dx
	call	Prompt                   ;Mostra il prompt 
	pop	dx
	mov	di, dx
	inc	dx
	call	GetString                ;Legge la stringa
	mov	[di], al                  ;Mette la lunghezza all'inizio della stringa
	pop	di
	ret

Replace:
	push	di
	test	[autoReplace?], -1       ;Se stiamo usando l'auto-replace,
	jz	@@L0
	jns	@@L2                      ; salta il controllo dello Shift
	mov	[autoReplace?], 1
	jmp	SHORT @@L1a               ;Chiedi la stringa se e' premuto shift e siamo al primo passaggio
@@L0:
	test	[justFound?], -1         ;Avvisa se non e' stata trovata la stringa
	jnz	@@L1
@@LE:
	pop	di
	jmp	na
@@L1:
	call	Shifted?                 ;Se non e' premuto Shift, chiedi la stringa
	jnz	@@L2			;per rimpiazzare la vecchia
@@L1a:
	mov	si, OFFSET replaceMsg
	mov	dx, OFFSET replaceString
	call	GetCountedString
@@L2:
	mov	si, OFFSET replaceString
	lodsb
	sub	ah, ah
	mov	dx, si
	push	ax
	push	dx
	sub	ch, ch                    ;Confronta la lunghezza delle 2 stringhe
	mov	cl, al
	sub	cl, [byte findString]     ;Se replaceString e' piu' lunga dell'altra,
	je	@@L6
	jb	@@L4
	xchg	dx, di
	call	EndLine                  ; assicurati che ci sia abbastanza spazio nella riga
	xchg	dx, di
	add	dl, cl
	cmp	dl, LINEWIDTH
	ja	@@LE
@@L3:
	call	Insert0                  ; quindi inserisci i caratteri in piu'
	loop	@@L3
	jmp	SHORT @@L6
@@L4:
	neg	cl                        ;Se e' piu' corta, elimina la differenza
@@L5:
	call	Delete0
	loop	@@L5
@@L6:
	pop	si                        ;Adesso sovrascrivi la vecchia stringa con quella nuova
	pop	cx
	sub	ch, ch
	pop	di
	rep	movsb
	jmp	NewLineC


;Mostra la schermata di aiuto
Help:
	push	es
	push	di

	mov	es, [videoSegment]
	mov	di, 00h
	mov	si, OFFSET helpMsg
	mov	cx, 80 * 25
	mov	ah, [attribNl]
@@L1:
	lodsb
	stosw
	loop	@@L1
	mov	si, OFFSET anyKeyMsg
	call	Print0

	mov	ah, 03h
	mov	bh,00h
	int	10h                       ; Nasconde il cursore  
	push	dx                       ; Spostandolo fuori dallo schermo
	push	cx
	mov	ah, 02h
	mov	dx,0FFFFh
	int	10h

	sub	ah, ah
	int	16h

	pop	cx
	pop	dx
	mov	ah, 01h
	int	10h               ; Ripristina il cursore

	pop	di
	pop	es
	ret

END Start1

