Tìm và thay thế ký tự non-ASCII trong MIPS

Chào mọi người,

Mình đang học môn kiến trúc máy tính trong kỳ học này và có được giao một bài tập như sau:

Write a program replacing all non-ASCII characters in a valid C source file with C-language
hexadecimal escape sequences. The program should be capable of producing valid C source from a C program containing non-ASCII characters embedded in strings and character constants.
All the text processing programs with file i/o should define getc and putc functions for single-character i/o, providing proper buffering of input and input operations with at least 512-byte buffers.

Theo mình hiểu được thì yêu cầu của bài sẽ là duyệt file C và thay thế các từ không thuộc bảng mã ASCII (code >127) sang dạng escape sequance là \x?? với ?? tương ứng với character code trong hệ 16.

Mình đã thực hiện được input filename từ bàn phím và đọc nội dung file vào buffer.
Mình muốn hỏi cách để tìm được ký tự nào có code > 127 trong MIPS và làm thế nào để replace được sang dạng hexadecimal escape sequences. Mà không dùng quá nhiều lệnh rẽ nhánh (If) trong chương trình.

Phía dưới đây là code của mình,
Rất cảm ơn nếu ai đó giúp được, bởi đây là một môn học rất khó đối với mình :sob:

.data
message: 	.asciiz "Please input file name \n"
userInput: 	.space  256
fileWords: .space 1024

.text
.globl main
main:
#ask user for input
	li $v0, 4
	la $a0, message
	syscall

#read user data/input from keyboard
	li $v0, 8
	la $a0, userInput
	li $a1, 256
	syscall

nameClean:
    	li $t0, 0       #loop counter
   	li $t1, 256      #loop end
clean:
    	beq $t0, $t1, L5
    	lb $t3, userInput($t0)
    	bne $t3, 0x0a, L6
    	sb $zero, userInput($t0)
L6:
    	addi $t0, $t0, 1
	j clean
L5:

#HOW TO READ INTO A FILE	
	li $v0,13           	# open_file syscall code = 13
    	la $a0, userInput     	# get the file name
    	li $a1,0           	# file flag = read (0)
    	syscall
    	move $s0,$v0        	# save the file descriptor. $s0 = file
	
	#read the file
	li $v0, 14		# read_file syscall code = 14
	move $a0,$s0		# file descriptor
	la $a1,fileWords  	# The buffer that holds the string of the WHOLE file
	la $a2,1024		# hardcoded buffer length
	syscall

	# print whats in the file
	li $v0, 4		# read_string syscall code = 4
	la $a0,fileWords
	syscall
	
	#Close the file
    	li $v0, 16         		# close_file syscall code
    	move $a0,$s0      		# file descriptor to close
    	syscall
1 Like

Tớ có 1 số câu hỏi và suggestion cho cậu về bài này.

  1. Sau khi đọc file và lưu vào filewords, cậu có thể đọc từng chữ cái trong filewords, validate từng chữ xem có phải ascii hay không (bằng hàm validate của cậu. Hàm này sẽ dễ thôi, cậu load ký tự vào thanh ghi và check giá trị thanh ghi đó có > 127 không). Nếu ko phải ascii thì replace chữ đó (bằng hàm replace của cậu, tớ nghĩ cậu đổi giá trị trong thanh ghi về hexa, rồi thêm 0x vào đầu, có lẽ thế là đủ).
  2. Đúng rồi đó cậu, chia để trị. Cậu nên viết hàm con để xử lý từng vấn đề nhỏ. Bao nhiêu rẽ nhánh sẽ không làm khó cậu nữa.
  3. Cậu cần viết comment cho tất cả các dòng (tớ có để ý mấy dòng đầu tiên cậu chưa viết). Tớ nghĩ đã có người nhấn mạnh việc này cho cậu rồi :slight_smile:
  4. Cẩn thận với pipeline hazard (tớ ko thấy có bất kỳ nop nào, đồng thời cậu ko có chỉ thị phù hợp cho chương trình để tự insert nop nếu cần, nên tớ đưa cảnh báo đó cho cậu).

Hi vọng suggestion trên giúp cậu. Bài này không khó tới mức đó đâu cậu :slight_smile: cố lên nhé!

4 Likes

Hi @library, cảm ơn cậu đã giúp tớ bằng những gợi ý quý giá.
Trước đó tớ còn không biết là có thể code các hàm trong MIPS, nhưng hóa ra nó không quá khó tiếp cận như tớ nghĩ.
Sau khi đọc các gợi ý của cậu, tớ đang code phần tìm kiếm các ký tự > 127 (0x7f trong hexadecima) và đếm các ký tự đó trước:

.data
toWrite: .asciiz "Hello World was here"
message: 	.asciiz "Please input file name \n"
userInput: 	.space  256
fileWords: .space 1024

.text
.globl main
	
main:
	# ask user for input
	li $v0, 4
	la $a0, message
	syscall
	# read user data/input from keyboard
	li $v0, 8
	la $a0, userInput
	li $a1, 256
	syscall

	jal nameClean
	jal readFile
	jal nonASCII
	jal printFile
	jal closeFile
	# clear the 0x0a from user Input to make it ready		
nameClean:
    	li $t0, 0       #loop counter
   	li $t1, 256      #loop end
clean:
    	beq $t0, $t1, L2
    	lb $t3, userInput($t0)
   	
    	bne $t3, 0x0a, L1
    	sb $zero, userInput($t0)
L1:
    	addi $t0, $t0, 1
	j clean
	
L2:	
	jr $ra

#count ASCII
nonASCII:
	li $t2, 0	#count non ASCII
    	li $t0, 0       #loop counter
   	li $t1, 1024      #loop end
count:
    	beq $t0, $t1, L4
    	lb $t3, fileWords($t0)
    	blt $t3, 0x7f, L3
    	addi $t2, $t2, 1
L3:
    	addi $t0, $t0, 1
	j count
	
L4:
	li $v0, 1
	move $a0, $t2
	syscall
	jr $ra

#READ A FILE	
readFile:		
	li $v0,13           	# open_file syscall code = 13
    	la $a0, userInput     	# get the file name
    	li $a1,0           	# file flag = read (0)
    	syscall
    	move $s0,$v0        	# save the file descriptor. $s0 = file
	
	#read the file
	li $v0, 14		# read_file syscall code = 14
	move $a0,$s0		# file descriptor
	la $a1,fileWords  	# The buffer that holds the string of the WHOLE file
	la $a2,1024		# hardcoded buffer length
	syscall
	jr $ra

# print whats in the file	
printFile:
	li $v0, 4		# read_string syscall code = 4
	la $a0,fileWords
	syscall
	jr $ra
	
#Close the file	
closeFile:
    	li $v0, 16         		# close_file syscall code
    	move $a0,$s0      		# file descriptor to close
    	syscall

File input của tớ gồm 5 ký tự cách nhau bởi một dấu cách:

€ ‚ ƒ „ …	

Output mong muốn của tớ là ra được số 5 tương ứng với 5 ký tự này, nhưng bộ đếm chỉ ra được 0.
Cậu xem giúp tớ với, và làm thế nào để đổi giá trị trong thanh ghi về hệ số 12 hả cậu? để tớ hoàn thiện hàm replace :smiley:

Cảm ơn cậu đã giúp đỡ.

2 Likes

Hi @minh171195,

Hexa là hệ 16 cậu à :smiley:
Về cơ bản, giá trị trong thanh ghi của cậu là hệ nhị phân.
Để đổi từ hệ nhị phân sang hệ 16 (2^4), cậu chỉ đơn giản đọc lần lượt 4 chữ số của số nhị phân và chuyển sang hệ 16 tương ứng.
Ví dụ: chuyển số 0011011010110000 sang hệ 16

  • Chia thành các bộ 4 chữ số: 0011 0110 1011 0000
  • Đổi từng bộ 4 chữ số sang hệ 16:
    • 0011 = 3
    • 0110 = 6
    • 1011 = B
    • 0000 = 0
  • Số hệ 16 của cậu là 0x36B0

Với giải thuật trên, cậu có thể implement hàm chuyển đổi 1 cách dễ dàng, phải không?
Hi vọng nó sẽ giúp cậu.

EDIT: Không may, cách cậu implement hàm sai hết rồi :frowning: Textbook của cậu không chỉ cách implement nó à cậu? :frowning:

4 Likes

Cậu ơi, cái file của tớ có nội dung như sau:

€ ‚ ƒ „ …

Theo như tớ search thì đây đều là các ký tự có code >127, nhưng khi load vào register nó đều hiện giá trị âm như là: -30, -126, -84, -128, -102 , -58, -110 mà chỉ có 5 ký tự thôi nhưng load vào thanh ghi lại ra rất nhiều giá trị khác nhau.
Hay như khi tớ đổi nội dung file thành:

Ç ü é â ä

Thì trong thanh ghi sẽ lưu lần lượt giá trị như sau :
-61, -121, 32, -61, -68, 32, -61, -87, 32, -61, -94, 32, -61, -92, 0, 0
Tớ nhận ra có sự lặp lại của mã -61 liên tục sau mỗi mã 32 là space, trừ số đầu tiên ra.
Việc này khiến cho hàm đếm của tớ không đúng được.
Khiến tớ đang bị tắc, cảm ơn cậu!
Tớ cũng không biết trong sách có không nữa, vì thực sự tớ không có sách còn bài giảng thì không có tý gì bởi đây là tiết LAB và phải tự làm.

Hi cậu,

Cậu có thể chụp màn hình cho tớ phần thanh ghi mà cậu bảo được không? Hoặc bất cứ hình ảnh gì chứng minh việc load byte vào thanh ghi mà có giá trị âm.
Về cơ bản, 1 thanh ghi có 4 byte, tuy nhiên mỗi ký tự có 1 byte, nên tớ nghĩ không có khả năng nào cậu có thể load 1 byte vào thanh ghi 4 byte mà làm cho bit dấu bằng 1 được :slight_smile:

Ngoài ra, tớ nghĩ có lẽ có hiểu nhầm ở đây, vì cậu đưa ra cho tớ 7 giá trị âm tương ứng với nội dung 5 chữ cái (hay 9?).

3 Likes

Nội dung file của tớ:
Ç ü é â ä
Đoạn code tớ đang test:

nonASCII:
	li $t2, 0	#count non ASCII
    	li $t0, 0       #loop counter
   	li $t1, 15      #loop end
count:
    	beq $t0, $t1, L4
    	lb $t3, fileWords($t0)
    	
    	bgt $t3, 0x7f, L3
    	addi $t0, $t0, 1
    	j count
    	
L3:	
	addi $t0, $t0, 1
    	addi $t2, $t2, 1
	j count
	
L4:
	li $v0, 1
	move $a0, $t2
	syscall
	jr $ra

Giá trị các thanh ghi ở vòng lặp đầu tiên:
Screen Shot 2020-05-18 at 11.44.52
Các giá trị tiếp theo của t3 sau ở các vòng lặp sau hình trên: -121, 32, -61, -68, 32, -61, -87, 32, -61, -94, 32, -61, -92, 0, 0
Còn t0 thì tịnh tiến đến 15, t2 vẫn = 0 bởi không có giá trị nào >127 cả.
Đây là kết quả của cả chương trình: Số 0 là số ký tự >127 đếm được, còn lại là nội dung file in ra trên MARS simulator.
Screen Shot 2020-05-18 at 11.47.22
Dạng text, t cop từ Run I/O:
0Ç ü é â ä
Đây là tớ check code đoạn đó trên character code finder:
Screen Shot 2020-05-18 at 11.50.58
Cảm ơn cậu nhiều lắm ạ!

1 Like

Seriously dude?

lb $t3, fileWords($t0)

Cậu load nhầm rồi cậu ơi.
Để lấy được từ space fileWords, cậu cần load address của nó trước, rồi mới lấy giá trị ra:

la $s0, fileWords   # Load the address of fileWords into s0
add $t4, $s0, $t0   # Load the address of A[i] (t4) = address(A) (s0) + i (t0)
lb $t3, 0($t4)      # Load a character at address A[i] and store it to t3
nop                 #Prevent pipeline hazard

Tớ nghĩ cậu cần đọc kỹ textbook trước khi làm, nếu không, cậu sẽ gặp rất nhiều vấn đề đấy! :slight_smile:
Giờ cậu thử sửa lại, rồi debug lại xem sao. Và nhớ comment từng dòng một giúp tớ, không phải tất cả mọi người ở đây đều đọc code MIPS như đọc văn đâu cậu :slight_smile:

4 Likes

Kinh nghiệm khi làm việc với non-ascii thi nó sẽ luôn có 3 thứ đi kèm với nhau: OS locale, font setting và file encoding.

Hint nhẹ dãy số bạn đang có:

>>> list(map(lambda c: c - 256 if c>127 else c, 'Ç ü é â ä'.encode('utf-8')))
[-61, -121, 32, -61, -68, 32, -61, -87, 32, -61, -94, 32, -61, -92]

>>> list(map(int, 'Ç ü é â ä'.encode('utf-8')))
[195, 135, 32, 195, 188, 32, 195, 169, 32, 195, 162, 32, 195, 164]
5 Likes

Hic, xin lỗi cậu vì đã đăng đoạn code mà không đầy đủ.
Tổng thể đoạn code của tớ là thế này sau khi đã cóc cậu giúp đỡ:

.data
toWrite: .asciiz "Hello World was here"
message: 	.asciiz "Please input file name \n"
userInput: 	.space  256
fileWords: .space 1024

.text
.globl main
	
main:
	# ask user for input
	li $v0, 4
	la $a0, message   #show mesager asking for input
	syscall
	# read user data/input from keyboard
	li $v0, 8.               #take the input
	la $a0, userInput  #load input into a0
	li $a1, 256.           #hardcode input length
	syscall

	jal nameClean    #remove newline from input
	jal readFile         #read file
	jal nonASCII      #count non ASCII character
	jal printFile        #print out file
	jal closeFile       #close the file

# clear the 0x0a from user Input to make it ready		
nameClean:
    	li $t0, 0       #loop counter
   	li $t1, 50      #loop end
clean:
    	beq $t0, $t1, L2         #end loop condition
    	lb $t3, userInput($t0) # load userInput($t0) into $t3
   	
    	bne $t3, 0x0a, L1      #if $t3 != new line, go to L1
    	sb $zero, userInput($t0)  #store userInput($t0)
L1:
    	addi $t0, $t0, 1        #counter +1
	j clean                     #continue loop
	
L2:	
	jr $ra

#count ASCII
nonASCII:
	li $t2, 0	#count non ASCII
    	li $t0, 0       #loop counter
   	li $t1, 15      #loop end
count:
    	beq $t0, $t1, L4     #end loop condition
    	la $s0, fileWords   # Load the address of fileWords into s0
	add $t4, $s0, $t0   # Load the address of A[i] (t4) = address(A) (s0) + i (t0)
	lb $t3, 0($t4)      # Load a character at address A[i] and store it to t3
	nop                 #Prevent pipeline hazard
    	bgt $t3, 0x7f, L3   # branch if character code >127 (0x7f)
    	addi $t0, $t0, 1    # loop counter + 1
    	j count		    # continue loop
    	
L3:	
	addi $t0, $t0, 1    # loop counter +1
    	addi $t2, $t2, 1    # nonASCII counter +1
	j count		    # continue loop
	
L4:
	li $v0, 1
	move $a0, $t2
	syscall
	jr $ra

#READ A FILE	
readFile:		
	li $v0,13           	# open_file syscall code = 13
    	la $a0, userInput     	# get the file name
    	li $a1,0           	# file flag = read (0)
    	syscall
    	move $s0,$v0        	# save the file descriptor. $s0 = file
	
	#read the file
	li $v0, 14		# read_file syscall code = 14
	move $a0,$s0		# file descriptor
	la $a1,fileWords  	# The buffer that holds the string of the WHOLE file
	la $a2,1024		# hardcoded buffer length
	syscall
	jr $ra

# print whats in the file	
printFile:
	li $v0, 4		# read_string syscall code = 4
	la $a0,fileWords #load address fileWords into $a0
	syscall
	jr $ra
	
#Close the file	
closeFile:
    	li $v0, 16         		# close_file syscall code
    	move $a0,$s0      		# file descriptor to close
    	syscall

Giá trị của Thanh ghi $t3 chứa từng byte của fileWords vẫn là: -61, -121, 32, -61, -68, 32, -61, -87, 32, -61, -94, 32, -61, -92, 0, 0
Tớ đang nghĩ hay do máy tính của tớ đọc font từ file không đúng nên bị như vậy :frowning:

2 Likes

@Stanley00 Nice hint! Tớ đã hoàn toàn không nghĩ tới encoding :smiley:

@minh171195
Từ lời gợi ý của Stanley, tớ đã hiểu vấn đề của cậu rồi. Source code cậu đọc vào space sử dụng UTF-8 encoding. Tớ nghĩ cậu cần đọc về spec của UTF-8 để hiểu cách lấy thông tin.
Tớ sẽ tóm tắt giải thuật để cậu chuyển từ chuỗi nhị phân sang dạng hexa:

  • Cậu đọc từng byte từ space source code.
    • Nếu MSB (bit đầu tiên bên trái của byte) là 0, đó là ký tự ASCII, cậu đọc tiếp byte tiếp theo
    • Nếu MSB khác 0
      • Nếu 3 bit đầu tiên của byte đó là 110 (gọi byte này là byte thứ nhất)
        • Cậu đọc byte tiếp theo (gọi là byte thứ 2)
        • Lấy 5 bit cuối của byte thứ nhất gộp với 6 bit cuối của byte thứ 2, rồi chuyển số đó sang hexa.
        • Đọc byte tiếp theo
      • Nếu 4 bit đầu tiên của byte đó là 1110 (gọi byte này là byte thứ nhất)
        • Cậu đọc 2 byte tiếp theo (gọi là byte thứ 2 và byte thứ 3)
        • Lấy 4 bit cuối của byte thứ nhất gộp với 6 bit cuối của byte thứ 2, rồi gộp với 6 bit cuối của byte thứ 3, rồi chuyển số đó sang hexa.
        • Đọc byte tiếp theo
      • Nếu 5 bit đầu tiên của byte đó là 11110 (gọi byte này là byte thứ nhất)
        • Cậu đọc 3 byte tiếp theo (gọi là byte thứ 2, byte thứ 3 và byte thứ 4)
        • Lấy 3 bit cuối của byte thứ nhất gộp với 6 bit cuối của byte thứ 2, gộp tiếp với 6 bit cuối của byte thứ 3, rồi gộp tiếp với 6 bit cuối của byte thứ 4, rồi chuyển số đó sang hexa.
        • Đọc byte tiếp theo

Tớ khuyên cậu đọc kỹ spec của UTF-8 (hoặc trên wiki cũng ổn) để hiểu rõ về cách chuyển đổi. Từ đó, cậu sẽ hiểu giải thuật chuyển dãy byte sang Unicode.

4 Likes

Trước khi viết MIPS thì bạn nên viết 1 program tương đương trên C. Khi có program rồi thì dựa vào C code để chuyển sang MIPS.

Đó là lời khuyên của mình.

3 Likes

Cậu @library ơi,
Cảm ơn cậu đã chỉ cho tớ về giải thuật chuyển đổi, thế nhưng khi tớ hỏi thầy giá về vấn đề encoding thì thầy có nói là hãy sử dụng các file encoded dạng Windows 1250 hoặc 1252.
Nếu như vậy thì giải thuật có thay đổi gì không hả cậu? Tớ nghĩ là sẽ chỉ cần chuyển sang hexa nữa thôi nhỉ, bởi vì đã detect được các non ASCII rồi :thinking:
Tớ vừa dùng notepad để tạo một file với encoding dạng trong Notepad image
Với nội dung như sau: € ‚ „ … †
Đây đều là những ký tự có giá trị >128 trong bảng encoding của windows 1252 có thứ tự character code tương ứng là: 128, 130, 132, 133, 134
Thì trong MARS đã đọc được đúng số ký tự và đếm ra được là 5 ký tự có mã <0 bởi đã trừ đi 256 khi load vào (theo hint của bạn @Stanley00) . Với giá trị ở trong thanh ghi tương ứng như sau: -128, -126, -124, -123, -122.

Tuy rằng Mars không hiển thị được ra nội dung của file trong phần I/O nhưng đã đếm được đúng số ký tự rồi. Giờ tớ phải tìm cách convert các ký tự đó sang hexadecial escape sequence nữa.
Đây là nội dung mà Mars hiện ra trong phần I/O với 5 là số ký tự <0 theo đúng như vòng lặp tớ setup:
image

Tớ đã thực hiện phần rẽ nhánh để check MSB và các bit trong byte đầu tiên của từng ký tự trong file theo code sau:

.data
toWrite: .asciiz "Hello World was here"
message: 	.asciiz "Please input file name \n"
userInput: 	.space  256
fileWords: 	.space 1024

.text
.globl main
	
main:
	# ask user for input
	li $v0, 4
	la $a0, message
	syscall
	# read user data/input from keyboard
	li $v0, 8
	la $a0, userInput
	li $a1, 256
	syscall

	jal nameClean
	jal readFile
	jal nonASCII
	jal printFile
	jal closeFile
	# clear the 0x0a from user Input to make it ready		
nameClean:
    	li $t0, 0       #loop counter
   	li $t1, 50      #loop end
clean:
    	beq $t0, $t1, L2
    	lb $t3, userInput($t0)
   	
    	bne $t3, 0x0a, L1
    	sb $zero, userInput($t0)
L1:
    	addi $t0, $t0, 1
	j clean
	
L2:	
	jr $ra

#count ASCII
nonASCII:
	li $t2, 0	#count non ASCII
    	li $t0, 0       #loop counter
   	li $t1, 15      #loop end
count:
    	beq $t0, $t1, L10
    	la $s0, fileWords   # Load the address of fileWords into s0
	add $t4, $s0, $t0   # Load the address of A[i] (t4) = address(A) (s0) + i (t0)
	lb $t3, 0($t4)      # Load a character at address A[i] and store it to t3
	nop                 #Prevent pipeline hazard
	srl $t5, $t3, 31    #shift 31 bit to right (mean take first bit)
    	bnez $t5, L3   # branch if MSB != 0 == non ASCII character
    	addi $t0, $t0, 1    # loop counter + 1
    	j count		    # continue loop
    	
L3:	
	srl $t5, $t3,30     # shift 30 bit to right (mean take second bit)  
	bne $t5, 2, L4	    # branch if second bit !=0 (first 2 bit != 10)
	addi $t0, $t0, 1
	j count		    # continue loop
	
L4:
	srl $t5, $t3, 29   # take third bits
	beq $t5, 6, L5	   # go to case 110 (first 3 bits = 110)
	bne $t5, 6, L6       # go to next branch (first 3 bits != 110)
	addi $t0, $t0, 1
	j count		   #continue loop

#case 110
L5: 
	addi $t0, $t0, 1		
	j count		#continue loop
L6:
	srl $t5, $t3, 28   # take fourth bits
	beq $t5,14, L7   # go to case 1110 (first 4 bits = 1110)	
	bne $t5,14,  L8   # go to next branch (first 4 bits != 1110)
	addi $t0, $t0, 1
	j count		#continue loop

#case 1110
L7:
	addi $t0, $t0, 1
	j count 	#continue loop
	
L8:
	srl $t5, $t3, 27  #take 5th bits
	beq $t5, 30, L9	  # go to case 11110 (first 5 bits = 11110)
	addi $t0, $t0, 1
	j count

#case 11110
L9:
	addi $t0, $t0, 1
	j count																
												
L10:
	jr $ra

#READ A FILE	
readFile:		
	li $v0,13           	# open_file syscall code = 13
    	la $a0, userInput     	# get the file name
    	li $a1,0           	# file flag = read (0)
    	syscall
    	move $s0,$v0        	# save the file descriptor. $s0 = file
	
	#read the file
	li $v0, 14		# read_file syscall code = 14
	move $a0,$s0		# file descriptor
	la $a1,fileWords  	# The buffer that holds the string of the WHOLE file
	la $a2,1024		# hardcoded buffer length
	syscall
	jr $ra

# print whats in the file	
printFile:
	li $v0, 4		# read_string syscall code = 4
	la $a0,fileWords    
	syscall
	jr $ra
	
#Close the file	
closeFile:
    	li $v0, 16         		# close_file syscall code
    	move $a0,$s0      		# file descriptor to close
    	syscall

Ở trong các vị trí xử lý các case 110, 1110 và 11110 tớ chưa hoàn thiện được, bởi tớ chưa tìm ra cách để ghép nối các bit như thế nào trong gợi ý của cậu. Cũng như chưa code được hàm đổi sang hệ 16 bởi tớ muốn hỏi cậu thêm về 2 cái này.

Lấy 5 bit cuối của byte thứ nhất gộp với 6 bit cuối của byte thứ 2, rồi chuyển số đó sang hexa.

Về chuyển đổi sang hệ hex thì, làm sao tớ chuyển được từng bộ 4 chữ số sang được dạng hex như gợi ý của cậu? Theo như tớ search được trên mạng thì có phải là rotate sang trái 4 bit rồi lấy số đó and với 0xf (1111), nếu kết quả <=9 thì sẽ cộng thêm 48 còn không thì sẽ cộng thêm 55.
Giải thuật như vậy đã đúng chưa hả cậu? :smiley:

  • Chia thành các bộ 4 chữ số: 0011 0110 1011 0000
  • Đổi từng bộ 4 chữ số sang hệ 16:
  • 0011 = 3
  • 0110 = 6
  • 1011 = B
  • 0000 = 0
  • Số hệ 16 của cậu là 0x36B0

Cảm ơn cậu và mọi người rất nhiều.

2 Likes

Nếu thầy đã nói chỉ áp dụng với file encoding Windows 1250/1252 thì bạn không cần xử lý như kiểu utf-8 làm gì cả, vì 2 cái encode khác nhau mà.

Với 1250/1252 thì đơn giản hơn nhiều vì tất cả các ký tự chỉ có 1 byte. Nên giải thuật tổng quát là vầy:

Đọc 1 ký tự từ file vào biến ch
Nếu 0<= ch <= 127: xuất ra ch // Mã ASCII hợp lệ nằm trong khoảng 0~127
Ngược lại:
  - Xuất ra '\x'
  - Xuất ra mã hex của ch

Chỗ này bạn cũng không cần trừ 256 làm gì để ra số âm đâu, nếu bạn có thể đọc được giá trị lớn hơn 127 thì cứ dùng nó để chuyển qua mã hex sẽ dễ hơn

4 Likes

Cảm ơn cậu, @Stanley00
Tớ thấy load vào thanh ghi nó vẫn là số âm cậu ạ vậy nên tớ mới check nó <0 thì sẽ là non ASCII, không biết làm thế nào để nó hiện được >127 nữa.
Tớ nghĩ đây là vấn đề của MARS simulator hoặc của Java.
Khi load số âm đó và chuyển nó sang hex thì tớ có nên cộng thêm 256 trước đó vào để nó hiện đúng giá trị thật rồi mới chuyển sang hex không cậu?

@library@Stanley00 ơi,

Hiện tớ đã code được phần chuyển 4 bit đầu và 4 bit cuối sang hex khi những bit đó có value <= 9 nghĩa là dạng số.
Đây là code của tớ:

.data
toWrite: .asciiz "Hello World was here"
message: 	.asciiz "Please input file name \n"
userInput: 	.space  256
fileWords: 	.asciiz ""

.text
.globl main
	
main:
	# ask user for input
	li $v0, 4
	la $a0, message
	syscall
	# read user data/input from keyboard
	li $v0, 8
	la $a0, userInput
	li $a1, 256
	syscall

	jal nameClean
	jal readFile
	jal nonASCII
	jal printFile
	jal closeFile
	# clear the 0x0a from user Input to make it ready		
nameClean:
    	li $t0, 0       #loop counter
   	li $t1, 50      #loop end
clean:
    	beq $t0, $t1, L2
    	lb $t3, userInput($t0)
    	bne $t3, 0x0a, L1
    	sb $zero, userInput($t0)
L1:
    	addi $t0, $t0, 1
	j clean
	
L2:	
	jr $ra

#count ASCII
nonASCII:
    	li $t0, 0       #loop counter
   	li $t1, 10      #loop end
   	li $t5, 0
count:
    	beq $t0, $t1, L4	#end loop condition
    	la $s0, fileWords	#load address buffer
    	lb $t3, fileWords($t0)	#load byte from file
	addi $t5, $t3, 0	#move byte value to $t5
	nop              	
    	bltz $t3, L3   		# branch if <0 (non ASCII case character code >127 will reduce 256)
    	bgt $t3, 0x7f, L3	#branch if >127 (non ASCII)
    	addi $t0, $t0, 1    	# loop counter + 1
    	j count		   	 # continue loop
    	
L3:	
	addi $t5, $t5, 256	#add 256 for correct value
	and $t6, $t5, 0xf	# mask with 1111 for taking last 4 bits
	and $t7, $t5, 0xf0	# mask with 11110000 
	srl $t7, $t7, 4		#shift right 4 bits to get first 4 bits of character
	ble $t6, 9, Sum		# if last 4 bit less than or equal 9, go to Sum
	b end
	
L4:
	jr $ra
Sum:
	ble $t7, 9 , Sum1	#if first 4 bit less than or equal 9, go to Sum1
	
Sum1:
	li $t8, 10		#put value 10 into $t8
	mul $t7, $t7, $t8	#multiple $t7 with 10 ($t8)
	addu $t7, $t7, $t6	#add $t7 with $t6
	b end			#go to end
end:
	sb $t7, fileWords($t0)	#store $t7 into buffer
	addi $t0, $t0, 1	#count+1
	j count			#continue 
#READ A FILE	
readFile:		
	li $v0,13           	# open_file syscall code = 13
    	la $a0, userInput     	# get the file name
    	li $a1,0           	# file flag = read (0)
    	syscall
    	move $s0,$v0        	# save the file descriptor. $s0 = file
	
	#read the file
	li $v0, 14		# read_file syscall code = 14
	move $a0,$s0		# file descriptor
	la $a1,fileWords  	# The buffer that holds the string of the WHOLE file
	la $a2,1024		# hardcoded buffer length
	syscall
	jr $ra

# print whats in the file	
printFile:
	li $v0, 4		# read_string syscall code = 4
	la $a0,fileWords    
	syscall
	jr $ra

writeFile:
	li $v0,15		# write_file syscall code = 15
    	move $a0,$s1		# file descriptor
    	la $a1,fileWords		# the string that will be written
    	la $a2,1024		# length of the toWrite string
    	syscall		
#Close the file	
closeFile:
    	li $v0, 16         		# close_file syscall code
    	move $a0,$s0      		# file descriptor to close
    	syscall

Tớ có thêm những câu hỏi sau để giúp tớ hoạn thiện được bài này:

  • Làm thế nào để tớ lưu giá trị trên thanh ghi theo dạng dạng string (nghĩa là thanh ghi có giá trị 80 thì sẽ lưu ‘80’) vào biến để thay thế cho byte đang duyệt?

Ví dụ byte đang duyệt có ký tự là có mã là 128 thì tớ đã có giá trị thanh ghi $t7 là 80 tương ứng với 128 trong hệ hexa rồi. Giờ làm thế nào để tớ có thể đưa ‘\x’ vào và tiếp theo là giá trị 80 của thanh ghi $t7, rồi dùng cụm đó thay thế cho ký tự ?
Tớ đang dùng store byte nhưng kết quả ra không đúng như tớ mong đợi. € ‚ „ … † hiện thành P R T U V chứ không phải là 80 82 84 85 86 như tớ muốn. Đây là hình ảnh:

Screen Shot 2020-05-19 at 20.48.47

  • Đối với 4 bit đầu và cuối nếu giá trị >9 thì tớ phải làm thế nào để biến nó thành dạng ‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’ trong dạng hexa hả cậu?

Cảm ơn 2 cậu rất nhiều!

1 Like

Về thuật cách chuyển thì không phải bạn đã nói tìm được ở đây rồi sao?
Mình cứ có cảm giác bạn làm phần này lại quên mất phần kia, đây là dấu hiệu bạn không có một thiết kế rõ ràng mà cứ cắm mặt vào code, mà lại code bằng assembly nên nó chắc chắn sẽ chỉ thành một mớ spagetty mà thôi.

5 Likes

Hi, cảm ơn cậu.
Tớ quên mất là mình đã có giải thuật cho phần chuyển đổi sang hex.
Input của tớ: € ‚ „ … † có mã là 128 130 132 133 134
Giờ output của tớ đã là :
0 0 0 0 0 0 8 0 0 0 0 0 0 0 8 2 0 0 0 0 0 0 8 4 0 0 0 0 0 0 8 5 0 0 0 0 0 0 8 6
Tương ứng với: 00000080 00000082 00000084 00000085 00000086
Tớ muốn hỏi làm cách nào để tớ nối các giá trị riêng lẻ với nhau bởi đây là tớ xuất ra giá trị của thanh ghi trong vòng lặp nên từng vòng sẽ chỉ ra một số duy nhất.
Và cách để thêm ‘\x’ ở đầu nữa sau đó cho vào buffer để cuối cùng tớ viết buffer đó vào file ban đầu để hoàn thiện chương trình.

Đây là code của tớ:

.data
toWrite: .asciiz "Hello World was here"
message: 	.asciiz "Please input file name \n"
userInput: 	.space  256
fileWords: 	.asciiz ""
newWords:	.asciiz "\x"
.text
.globl main
	
main:
	# ask user for input
	li $v0, 4
	la $a0, message
	syscall
	# read user data/input from keyboard
	li $v0, 8
	la $a0, userInput
	li $a1, 256
	syscall

	jal nameClean
	jal readFile
	jal nonASCII
	#jal printFile
	jal closeFile
	# clear the 0x0a from user Input to make it ready		
nameClean:
    	li $t0, 0       #loop counter
   	li $t1, 50      #loop end
clean:
    	beq $t0, $t1, L2
    	lb $t3, userInput($t0)
    	bne $t3, 0x0a, L1
    	sb $zero, userInput($t0)
L1:
    	addi $t0, $t0, 1
	j clean
	
L2:	
	jr $ra

#count ASCII
nonASCII:
    	li $t0, 0       #loop counter
   	li $t1, 10      #loop end
count:
	li $t2, 0
	li $t5, 0
	li $t6, 0
    	beq $t0, $t1, L4	#end loop condition
    	la $s0, fileWords	#load address buffer
    	la $s1, newWords
    	lb $t3, fileWords($t0)	#load byte from file
	add $t5, $t5, $t3	#move byte value to $t5
	nop              	
    	bltz $t3, L33   	# branch if <0 (non ASCII case character code >127 will reduce 256)
    	bgt $t3, 0x7f, L333	#branch if >127 (non ASCII)
    	addi $t0, $t0, 1    	# loop counter + 1
    	j count		   	 # continue loop

L33:
	addi $t5, $t5, 256	#case <0 will add 256 back to correct the original charecter code
	add $t6, $t6, $t5	#move value to $t6
	b L3
L333:
	b L3	    	    	
L3:	
	beq $t2, 8, endend	#end loop for non ASCII character
	rol $t6, $t6, 4		#rotate left 4 bit
	and $t7, $t6, 0xf	# mask with 1111 for taking last 4 bits
	ble $t7, 9, Sum		# if last 4 bit less than or equal 9, go to Sum
	addi $t7, $t7, 0x37	# >9 will add 55
	b end
	
L4:
	jr $ra
Sum:
	addi $t7, $t7, 0x30	# <=9 will add 48
	sb $t7, newWords	
	b end
end:
	li $v0, 4		
	la $a0, newWords
	syscall
	addi $t2, $t2, 1
	j L3
	
endend:
	addi, $t0, $t0, 1
	j count
	
	

#READ A FILE	
readFile:		
	li $v0,13           	# open_file syscall code = 13
    	la $a0, userInput     	# get the file name
    	li $a1,0           	# file flag = read (0)
    	syscall
    	move $s0,$v0        	# save the file descriptor. $s0 = file
	
	#read the file
	li $v0, 14		# read_file syscall code = 14
	move $a0,$s0		# file descriptor
	la $a1,fileWords  	# The buffer that holds the string of the WHOLE file
	la $a2,1024		# hardcoded buffer length
	syscall
	jr $ra

# print whats in the file	
printFile:
	li $v0, 4		# read_string syscall code = 4
	la $a0,fileWords    
	syscall
	jr $ra

writeFile:
	li $v0,15		# write_file syscall code = 15
    	move $a0,$s1		# file descriptor
    	la $a1,fileWords		# the string that will be written
    	la $a2,1024		# length of the toWrite string
    	syscall		
#Close the file	
closeFile:
    	li $v0, 16         		# close_file syscall code
    	move $a0,$s0      		# file descriptor to close
    	syscall
1 Like

Để mình sửa lại “giải thuật” ở comment #4 trên kia chút xíu:

Lần cuối cùng mình nhắc nhở bạn, nếu bạn không có khả năng tư duy bằng ngôn ngữ asm, thì hãy làm ơn dùng mã giả/sơ đồ thuật toán/uml/C hay bất kỳ gì bạn thông thạo để tư duy trước khi nhảy vào code bạn nhé.

5 Likes

Đây là final code cho bài tập chuyển đổi các ký tự non ASCII sang dạng hexadecimal escape sequence. Code assembly:

.data
message: 	.asciiz "Please input file name with full paths and directories \n"
newLine:	.asciiz "\n"
userInput: 	.space  256
fileWords: 	.space 1024
newWords:	.space 10240

.text
.globl main
	
main:
	
	# ask user for input
	li $v0, 4
	la $a0, message
	syscall
	# read user data/input from keyboard
	li $v0, 8
	la $a0, userInput
	li $a1, 256
	syscall
	
	jal nameClean
	jal readFile
	jal nonASCII
	jal writeFile
	jal closeFile
	
# clear the new linw (0x0a) from user Input to make it ready		
nameClean:
    	li $t0, 0       #loop counter
   	li $t1, 256      #loop end
clean:
    	beq $t0, $t1, L2	#end loop condition
    	lb $t3, userInput($t0)
    	bne $t3, 0x0a, L1
    	sb $zero, userInput($t0)
L1:
    	addi $t0, $t0, 1
	j clean
	
L2:	
	jr $ra

#count ASCII
nonASCII:
    	li $t0, 0       #loop counter
   	li $t1, 1024      #loop end
   	
	addi $t8, $t8, 0x5c	#put '\' into $t8
	addi $t9, $t9, 0x78	#put 'x' into $t9
	la $s1, newWords
    	la $s0, fileWords	
count:
	li $t2, 0
	li $t5, 0
	li $t6, 0
	li $t3, 0
    	beq $t0, $t1, L4	#end loop condition
    	lb $t3, fileWords($t0)	#load byte from file
	add $t5, $t5, $t3	#move byte value to $t5
	nop
	beqz $t3, equal0        #branch if meet null character      	
    	bltz $t3, lessthan0   	# branch if <0 (non ASCII case character code >127 will reduce 256)
    	bgt $t3, 0x7f, morethan127	#branch if >127 (non ASCII)
    	sb $t3, newWords ($t4)
    	addi $t4, $t4, 1
    	addi $t0, $t0, 1    	# loop counter + 1
    	j count		   	 # continue loop

equal0:
	addi $t0, $t0, 1
	j count

lessthan0:
	sb $t8, newWords($t4)	#load '\' into buffer
	addi $t4, $t4, 1	
	sb $t9, newWords($t4)	#load 'x' into buffer
	addi $t4, $t4, 1
	addi $t5, $t5, 256	#case <0 will add 256 back to correct the original charecter code
	add $t6, $t6, $t5	#move value to $t6
	rol $t6, $t6,24
	b L3
morethan127:
	sb $t8, newWords($t4)	#load '\' into buffer
	addi $t4, $t4, 1
	sb $t9, newWords($t4)	#load 'x' into buffer
	addi $t4, $t4, 1
	add $t6, $t6, $t5	#move value to $t6
	rol $t6, $t6,24
	b L3	    	    	
L3:	
	beq $t2, 2, endend	#end loop for non ASCII character (take only 2 last characters)
	rol $t6, $t6, 4		#rotate left 4 bit
	and $t7, $t6, 0xf	# mask with 1111 for taking 4 bits in the right side
	ble $t7, 9, Sum		# if last 4 bit less than or equal 9, go to Sum
	addi $t7, $t7, 0x37	# >9 will add 55
	sb $t7, newWords($t4)	#save value into newWords
	addi $t4, $t4, 1
	b end
	
L4:
	jr $ra
Sum:
	addi $t7, $t7, 0x30	# <=9 will add 48
	sb $t7, newWords($t4)	#save value into newWords
	addi $t4, $t4, 1	#add 1 into new buffer counter
	b end
end:
	addi $t2, $t2, 1
	j L3
	
endend: 
	addi $t0, $t0, 1
	li $t5, 0
	lb $t5, fileWords($t0)
	beq $t5, 0x20, notaddquote	#check is that end of word
	beq $t5, 0x0a, notaddquote	#check is that end of line
	beq $t5, 0x22, notaddquote	#check is that next character is "
	beq $t5, 0x27, notaddquote	#check is that next character is '
	li $t5, 0
	addi $t5, $t5, 0x22		#add double quote to $t5
	sb $t5, newWords($t4)		#put double quote into new buffer
	addi $t4, $t4, 1		#add 1 into new buffer counter
	sb $t5, newWords($t4)		#put 1 more double quote into new buffer
	addi $t4, $t4, 1		#add 1 into new buffer counter
	j count
	
notaddquote:
	j count
	
	

#READ A FILE	
readFile:
	#open file		
	li $v0,13           	# open file code = 13
    	la $a0, userInput     	#  the file name
    	li $a1,0           	# file flag read (0)
    	syscall
    	move $s0,$v0        	# file descriptor. $s0 = file

	#read file
	li $v0, 14		# read file code = 14
	move $a0,$s0		# file descriptor
	la $a1,fileWords  	# The buffer that holds the string of the WHOLE file
	la $a2,1024		# hardcoded buffer length
	syscall
	
	#close file
	li $v0, 16         		# close file code
    	move $a0,$s0      		# file descriptor
    	syscall
    	jr $ra
    	
# print whats in the file (for test the program)	
#printFile:
#	li $v0, 4		# read string code = 4
#	la $a0,fileWords    
#	syscall
#	jr $ra

# write into file
writeFile:
	#open file
	li $v0,13           	# open file code = 13
    	la $a0, userInput     	# the file name
    	li $a1,1           	# file flag write (1)
    	syscall
    	move $s1,$v0 

	#write file
	li $v0,15		# write file code = 15
    	move $a0,$s1		# file descriptor
    	la $a1,newWords		# the string will be written
    	la $a2,10240		# length of the newWords string
    	syscall
    	jr $ra		
#Close the file	
closeFile:
	#print new buffer (for test the program)
	#li $v0, 4		
	#la $a0, newWords
	#syscall
	
	#close file
    	li $v0, 16         		# close file syscall code
    	move $a0,$s1      		# file descriptor
    	syscall

4 Likes
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?