;Scrivere un programma in assembler che:
;Accetti da tastiera parole (da 1 a 10 caratteri).
;Stampi su schermo la lista delle parole inserite in ordine di lunghezza.
;Il programma deve operare nel seguente modo:
;1. Presentarsi.
;2. Richiedere se si vuole:
;		inserire una parola.
;		stampare la lista delle parole già inserite.
;		uscire dal programma.
;Al termine dell'inserimento e della stampa il programma deve ripetere le operazioni previste al punto 2.
;-------------------------------------------------------------------------------------;
;	PROGETTO di CALCOLATORI ELETTRONICI A	       	    			  				  ;
;	docente Prof. Gianni Conte														  ;
;						         					 								  ;
;	Claudio Pitzalis 	  184949			           			                      ; 
;																					  ;
;-------------------------------------------------------------------------------------;
;Nota: in questa versione uso una modalità di reset delle variabili inserendo i valori di default
;scorrendo l'array della variabile byte per byte
;-------------------------------------------------------------------------------------;
; Qui definisco alcune costanti														  ;
;-------------------------------------------------------------------------------------;

MAX_NUM_WORDS		EQU 5   			;Limito il numero delle parole da inserire
LENGHT_WORD			EQU 10				;Lunghezza della parola: 10 caratteri
LENGHT_WORD_LR		EQU	12				;La stringa dovrà comprendere il rientro e l'avanzamento di linea
INSERT				EQU 13				; tasto invio
LINEFEED			EQU 10				; ritorno di linea

DSEG				SEGMENT PARA PUBLIC 'DATA'

PRESENT0			DB '*****************************************************************$'
PRESENT1 			DB '*PROGETTO di CALCOLATORI ELETTRONICI A                          *$'
PRESENT2			DB '*di Claudio Pitzalis                                            *$'
PRESENT3 			DB '*docente prof. Gianni Conte                                     *$'
EMPTY_LINE			DB '*                                                               *$'
PRESENT4 			DB '*Il programma accetta da tastiera parole da 1 a 10 caratteri    *$'
PRESENT5 			DB '*e stampa su schermo la lista delle parole                      *$'
PRESENT6			DB '*inserite in ordine di lunghezza.                               *$'
PRESENT7			DB '*****************************************************************$'
INSERT_MESS			DB	INSERT, LINEFEED, 'Inserire una parola: $'
MENU				DB	INSERT, LINEFEED, LINEFEED
					DB	'Scegli un''opzione:    I per inserire una parola,'
					DB	INSERT, LINEFEED
MENU_2				DB	'                      S per stampare le parole in ordine di lunghezza'
					DB	INSERT, LINEFEED
					DB	'                      R per resettare'
					DB	INSERT, LINEFEED
					DB	'                      U per uscire dal programma.'
EMPTY_LINE_1		DB	INSERT, LINEFEED, '$'
NO_WORDS_INSERT		DB  INSERT, LINEFEED, 'Nessuna parola inserita! $'
TOO_MANY_WORDS		DB  INSERT, LINEFEED, 'Superato il numero massimo di parole: scegli un''altra opzione $'
WORDS				DB	MAX_NUM_WORDS DUP (LENGHT_WORD DUP(' '), INSERT, LINEFEED)	;Creo un array di MAX_NUM_WORDS stringhe
																					;dove ogni stringa è inizializzata con LENGHT_WORD spazi, un invio
																					;e un avanzamento di linea
																					;Ovviamente il massimo num di parole inserite non potrà superare 256
LENGHTS			    DB	MAX_NUM_WORDS DUP ('?')
NUM_WORDS			DB	?

DSEG				ENDS

STACKM			SEGMENT PARA STACK 'STACK'	;Viene allocata memoria
				DB	64 DUP('12345678')		;per lo Stack: in tutto 64*8 bytes.
STACKM			ENDS						

				ASSUME CS:CSEG,DS:DSEG,SS:STACKM   
CSEG			SEGMENT PARA PUBLIC 'CODE'

;---------------------------------------------------------------------;
;																	  ;
;Corpo principale del programma										  ;
;																	  ;
;---------------------------------------------------------------------;
MAIN			PROC	FAR
				PUSH	DS									;Istruzioni da lasciare SEMPRE
				MOV		AX,00h								; al principio dei programmi!
				PUSH	AX
				
				CALL	BEGINNING							;inizializzazione
				CALL 	CLEAR_SCR							;cancellazione schermo
				CALL	PRESENTATION						;presentazione
	MAIN_2:		CALL  	INSTRUCTION	
				
	
			
		MAIN_LOOP: 			CALL	READ_CHOOSE			    ;Legge la scelta da tastiera chiamando la procedura relativa
															; e pone il risultato in AL
							CMP		AL, 'I'					;Se il tasto è 'I'
							JZ		I						; il flag ZF va a 1 e allora salta alla procedura di inserimento
							CMP		AL, 'i'					; e se non è 'i',
							JNZ		NO_INSERT				; il flag ZF va a 0 e allora salta alla procedura no_insert
		I:					CALL	INSERTING			    ;Procedura di inserimento
							CALL	ORDENING				;Procedura di ordinamento
							MOV		DX, OFFSET MENU			;Stampa le istruzioni
							CALL	PRINT_STRING
							JMP		MAIN_LOOP
				
		NO_INSERT: 			CMP AL, 'R' 					;Se il tasto è 'R', 
							JZ RESET 						 
							CMP AL, 'r' 					;o se il tasto è 'r'
							JZ RESET						;viene reinizializzato il programma
							CMP AL, 'S' 					;Se il tasto è 'S' o's'
							JZ PL 						    ;passa alla procedura di stampa lista
							CMP AL, 's' 					; 
							JNZ EXIT 						; altrimenti il tasto è 'U' o 'u', allora esce 
		PL:					CMP NUM_WORDS,0					;Va alla procedura di stampa lista solo se vi sono parole inserite
							JNE PL_2					
							MOV DX, OFFSET NO_WORDS_INSERT	; altrimenti stampa un messaggio di avviso
							CALL PRINT_STRING
		PL_2:				CALL PRINT_LIST 				;Chiamata alla procedura di stampa lista
							MOV DX, OFFSET MENU 			;stampa le istruzioni 
							CALL PRINT_STRING 
							JMP MAIN_LOOP 		    		;Ritorno al ciclo principale 
		RESET:				CMP NUM_WORDS,0					;Se ancora non è stata inserita una parola
							JE MAIN_LOOP					;allora il tasto di reset non viene preso in considerazione
							MOV CL, NUM_WORDS				;Ciclo di reset per riportare l'array delle parole al valore iniziale
							MOV SI, OFFSET WORDS
							MOV AL,' '
			LOOP_RST:		MOV CH, LENGHT_WORD
			LOOP_RST_1:		MOV [SI], AL					;Ciclo per sostituire i primi 10 caratteri dell'array con spazi 
							INC SI
							DEC CH
							CMP CH,0
							JNE LOOP_RST_1
							ADD SI, 2						;Incremento SI di due byte (insert e linefeed non sono stati toccati)
							DEC CL
							CMP CL,0
							JNE LOOP_RST					;Ripete il ciclo per tutte le stringhe precedentemente modificate
							MOV NUM_WORDS, 0				;Riporto a zero il numero di parole inserite
							CALL CLEAR_SCR
							JMP MAIN_2
		EXIT: 				RET								;Ritorno al Sistema Operativo 							
MAIN 			ENDP 
;---------------------------------------------------------------------;
;Procedura di inizializzazione                                        ;
;---------------------------------------------------------------------;
BEGINNING 	PROC NEAR
					MOV AX , SEG DSEG 
					MOV DS , AX 
					MOV NUM_WORDS, 0 						;Inizializzo a zero il numero di parole inserite 
					RET 
BEGINNING 	ENDP 

;-------------------------------------------------------------------------------;
;    Procedura per cancellare lo schermo e posizionarsi in (0,0)	            ;
;-------------------------------------------------------------------------------;

CLEAR_SCR 	PROC NEAR

          		PUSH AX
          		PUSH BX
          		PUSH CX
          		PUSH DX
         		MOV     AH, 6           	;Magic BIOS number.
          		MOV     AL, 0           	;Cancella l'intero schermo.
          		MOV     BH, 07          	;Clear with black spaces.
          		MOV     CX, 0000        	;Upper left corner is (0,0)
          		MOV     DL, 109          	;Colonne da cancellare
          		MOV     DH, 54          	;Righe da cancellare
          		INT     10h             	;Make the BIOS call.
          		MOV     AH,02h
				XOR     BH,BH
          		XOR     DX,DX
          		INT     10h
				POP     DX
				POP     CX
         		POP     BX
          		POP     AX

          		RET

CLEAR_SCR 	ENDP
;------------------------------------------------------------------------------;
; Procedura per andare a capo			                                       ;						                                                       ;
; Registri utilizzati: AH,DX				                                   ;
;------------------------------------------------------------------------------;
PRINT_CRLF 			PROC NEAR
					MOV AH,02h 				;Serzivio DOS 'Character Output'
					MOV DL,0DH    			;CR
					INT 21h      
					MOV AH,02h 
					MOV DL,0AH    			;LF
					INT 21h
					RET
PRINT_CRLF 			ENDP
;-------------------------------------------------------------------------------;
; Procedura per stampare una stringa e andare a capo		                    ;
;						                                                        ;
; L'indirizzo della stringa e' memorizzata in DS:DX	                            ;
; Registri utilizzati: AH,DX				                                    ;
;-------------------------------------------------------------------------------;

PRINT_STRING 	PROC NEAR

          		PUSH AX							;salvo il valore di AX nello STACK
          		MOV AH,09h						;Servizio DOS 'Print String'
          		INT 21h							;la stringa è puntata da DS:DX
          		CALL PRINT_CRLF					
         		POP AX							;Ripristino il valore precedente di AX
         		RET

PRINT_STRING 	ENDP
;-------------------------------------------------------------------------------;
;    Procedura per visualizzare la presentazione		                        ;
;-------------------------------------------------------------------------------;

PRESENTATION 	PROC NEAR

				CALL PRINT_CRLF
           		MOV DX,OFFSET PRESENT0
           		CALL PRINT_STRING
           		MOV DX,OFFSET PRESENT1
           		CALL PRINT_STRING
				MOV DX,OFFSET PRESENT2
           		CALL PRINT_STRING
           		MOV DX,OFFSET PRESENT3
           		CALL PRINT_STRING
				MOV DX,OFFSET EMPTY_LINE
           		CALL PRINT_STRING
				MOV DX,OFFSET PRESENT4
           		CALL PRINT_STRING
           		MOV DX,OFFSET PRESENT5
           		CALL PRINT_STRING
				MOV DX,OFFSET PRESENT6
           		CALL PRINT_STRING
           		MOV DX,OFFSET PRESENT7
           		CALL PRINT_STRING
           		RET

PRESENTATION 	ENDP
;-------------------------------------------------------------------------------;
;    Procedura per stampare il menù		                                        ;
;-------------------------------------------------------------------------------;
INSTRUCTION 		PROC NEAR 
					MOV DX, OFFSET MENU 					;inserisce nel registro DX il menu 
					CALL PRINT_STRING                       ;e chiama la procedura di stampa
					RET 
INSTRUCTION 		ENDP 
;------------------------------------------------------------------------------;
;Procedura per la lettura e verifica del tasto digitato                        ;
;------------------------------------------------------------------------------;
READ_CHOOSE 	PROC NEAR 
			
			NEW_DIGIT: 		MOV AH ,08h 					;Servizio DOS 'Read Keyboard Char 
							INT 21h 						; Without Echo ' 
							CMP AL ,'I' 					;Controlla se il tasto digitato è
							JZ RETURN
							CMP AL ,'i' 					;"I","i","S","s", "U", "u", "R" oppure "r"
							JZ RETURN
							CMP AL ,'R' 					
							JZ RETURN						
							CMP AL ,'r'
							JZ RETURN
							CMP AL ,'S' 					
							JZ RETURN						; Se il tasto è uno di questi allora
							CMP AL ,'s' 					
							JZ RETURN
							CMP AL ,'U' 					;viene mantenuto nel registro AL 
							JZ RETURN
							CMP AL ,'u' 					;e si ritorna al programma chiamante
							JNZ NEW_DIGIT					; diversamente ripete la procedura NEW_DIGIT 
															
			RETURN:		RET 

READ_CHOOSE 	ENDP 
;------------------------------------------------------------------------------;
;Procedura per il controllo e conteggio delle parole nell'array                ;
;e chiamata alla procedura di lettura                                          ;
;------------------------------------------------------------------------------;
INSERTING 		PROC NEAR 
				MOV BL, NUM_WORDS 
				CMP BL, MAX_NUM_WORDS 				    ;Se il numero di parole e' entro il massimo consentito
				JNZ CONT								;continua
				MOV DX , OFFSET TOO_MANY_WORDS
				CALL PRINT_STRING						;altrimenti stampa un messaggio di avviso
				JMP END_INSERT							; e ritorna
		CONT:	MOV DX , OFFSET INSERT_MESS 
				CALL PRINT_STRING 
				MOV AL , LENGHT_WORD_LR 
				MUL BL 
				ADD AX , OFFSET WORDS 
				MOV BX , AX 					        ;Punta alla prima parola vuota 
				CALL READ_WORD 							;Legge una parola 
				MOV BL, NUM_WORDS						;Pone in BL il numero di parole 
				XOR BH , BH 							;Azzera BH e memorizza la lunghezza 
				MOV [OFFSET LENGHTS+BX], DL   		    ; della parola inserita 
				INC NUM_WORDS							;Aggiorna il numero di parole
	END_INSERT:	RET 

INSERTING 		ENDP 

;----------------------------------------------------------------------------------;
;Procedura per la lettura, il controllo e l'inserimento dei caratteri in un array  ;
;BX punta alla memoria dove vengono immagazzinati i caratteri                      ;
;Viene calcolata la lunghezza della parola e inserita nel registro DX              ;
;----------------------------------------------------------------------------------;
READ_WORD 	 	PROC NEAR 
					
							MOV CX,	LENGHT_WORD 		;Inizializza il contatore caratteri 
		NEW_READING: 		MOV AH ,08h 				;Servizio DOS 'Read Keyboard Char 
							INT 21h 					; Without Echo ' 
							CMP AL, INSERT 				;Altrimenti se il tasto e' enter, 
							JE END_READING 			    ; termina la lettura 
							CMP AL, ' '					; E controlla che i caratteri siano entro certi valori
							JBE NEW_READING				; cioè superiori e diversi dal codice ASCII 32 (spazio)
							PUSH AX 					;Salva il registro AX nello STACK
							MOV AH,02h 					;Servizio DOS 'Character Output' 
							MOV DL, AL 					;Carattere da stampare 
							INT 21h 					;e stampa carattere
							POP AX 						;Ripristina il contenuto di AX 
							MOV [BX], AL 				;Memorizza il carattere digitato in un registro puntato da BX 
							INC BX 						;Aggiorna il puntatore 
							LOOP NEW_READING 			;Ripete il ciclo fino a che CX=0 (LOOP decrementa automaticamente CX)
		END_READING : 		CMP CX, LENGHT_WORD			; Se ancora non è stata inserito un carattere
							JE	NEW_READING				;non viene considerato il tasto di invio e torna alla lettura
							MOV DX, LENGHT_WORD 		;Calcola in DX la lunghezza della 
							SUB DX, CX 					; parola inserita
							RET
					
READ_WORD 		ENDP 

;------------------------------------------------------------------------------;
;Procedura per per l'ordinamento delle parole secondo lunghezza                ;
;Vengono utilizzati i registri indice (puntatori) SI e DI                      ;
;------------------------------------------------------------------------------;
ORDENING 			PROC NEAR 
					MOV AL, NUM_WORDS 
					DEC AL 						        ;Decrementa AL 
					JZ END_ORDENING 					;Se AL era 1, termina la procedura 
					MOV CL, LENGHT_WORD_LR 
					MUL CL 
					MOV SI, OFFSET WORDS 				;SI punta all'inizio area parole 
					MOV DI, SI 							;Copia del puntatore SI nel puntatore DI
					ADD DI, AX 							;DI punta all'inizio dell'ultima parola 
					XOR BX, BX 							;Contatore parole 

		LOOP_ORDENING : 	CALL COMPARE 				;Chiamata alla procedura di confronto parole: 
														; se vanno scambiate ritorna 
														; settando il flag Carry 
							JNC NO_CHANGE 				;Se no, salta lo scambio 
							CALL CHANGE 				;Procedura di scambio parole 
		NO_CHANGE : 		INC BX 						;Incrementa contatore parole 
							ADD SI, LENGHT_WORD_LR 
							CMP SI, DI 					;Se il confronto non e' terminato 
							JNZ LOOP_ORDENING 			; ripete il ciclo di ordinamento
						
		END_ORDENING : 	RET 

ORDENING 		ENDP 

;------------------------------------------------------------------------------;
;Procedura per il confronto tra le lunghezze di due parole                     ;
;------------------------------------------------------------------------------;
COMPARE 			PROC NEAR 
					MOV AL, [OFFSET LENGHTS+BX] 					;Prima lunghezzza 
					PUSH BX 										;Salva BX nello STACK
					MOV BL, NUM_WORDS 
					XOR BH, BH 
					CMP AL, [OFFSET LENGHTS-1+BX] 					;Confronto con la seconda lunghezza (verrà o meno settato il flag di carry)
					POP BX 											;Ripristina BX
					RET 
COMPARE 			ENDP 

;------------------------------------------------------------------------------;
;Procedura per lo scambio di parole di lunghezze diverse                       ;
;------------------------------------------------------------------------------;
CHANGE 			PROC NEAR 
						PUSH SI 				        			;Salva nello STACK i registri indici (puntatori) SI e DI		
						PUSH DI					

		LOOP_CHANGE : 		MOV AL, BYTE PTR [SI] 					;Legge il prossimo carattere 
							CMP AL, INSERT 							;Se il tasto è INVIO 
							JZ CHANGE_LENGHT 						; termina lo scambio 
							MOV AH, BYTE PTR [DI] 
							MOV BYTE PTR [DI], AL 					;Procedura di scambio parole 
							MOV BYTE PTR [SI], AH 
							INC DI 									;Incrementa il puntatore DI 
							INC SI 									;Incrementa il puntatore SI 
							JMP LOOP_CHANGE 						;Ripeti per un altro carattere 
						
		CHANGE_LENGHT : 	MOV AL, [OFFSET LENGHTS+BX] 			;Prima lunghezza 
							PUSH BX 								;Ora scambia le lunghezze 
							MOV BL, NUM_WORDS 
							XOR BH, BH 
							MOV AH, [OFFSET LENGHTS-1+BX] 
							MOV [OFFSET LENGHTS-1+BX], AL 
							POP BX 
							MOV [OFFSET LENGHTS+BX], AH 
						
						POP DI 										;Ripristina il precedente contenuto di SI e DI
						POP SI 
							RET 
CHANGE 			ENDP 

;-----------------------------------------------------------------------------;
;Procedura per stampa della lista delle parole in ordine di lunghezza         ;
;-----------------------------------------------------------------------------;					
PRINT_LIST 			PROC NEAR 
					
					MOV DX, OFFSET EMPTY_LINE_1 
					CALL PRINT_STRING 								;Stampa una linea bianca 
					MOV DX, OFFSET WORDS 
					MOV AL, NUM_WORDS 
					MOV BL, LENGHT_WORD_LR 
					MUL BL 
					MOV BX, DX 
					ADD BX, AX 					                    ;Punta al termine della stringa 
					MOV CL, [BX] 				                    ;Salva il contenuto di [BX]
					MOV BYTE PTR [BX], '$' 		                    ;Segnala il termine per la 
					CALL PRINT_STRING 			                    ; procedura di stampa 
					MOV [BX], CL 				                    ;Ripristina il contenuto di [BX] 
					RET 

PRINT_LIST 			ENDP 

;-----------------------------------------------------------------------------;
;Termine del programma                                                        ;
;-----------------------------------------------------------------------------;	
CSEG 				ENDS 
					END MAIN
					
