1 ; =============================================================================
2 ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems
3 ; Copyright (C) 2008-2011 Return Infinity -- see LICENSE.TXT
6 ; =============================================================================
13 ; -----------------------------------------------------------------------------
14 ; os_int_to_string -- Convert a binary interger into an string
15 ; IN: RAX = binary integer
16 ; RDI = location to store string
17 ; OUT: RDI = points to end of string
18 ; All other registers preserved
19 ; Min return value is 0 and max return value is 18446744073709551615 so your
20 ; string needs to be able to store at least 21 characters (20 for the digits
21 ; and 1 for the string terminator).
22 ; Adapted from http://www.cs.usfca.edu/~cruse/cs210s09/rax2uint.s
29 mov rbx
, 10 ; base of the decimal system
30 xor ecx, ecx ; number of digits generated
31 os_int_to_string_next_divide:
32 xor edx, edx ; RAX extended to (RDX,RAX)
33 div rbx
; divide by the number-base
34 push rdx
; save remainder on the stack
35 inc rcx
; and count this remainder
36 cmp rax
, 0 ; was the quotient zero?
37 jne os_int_to_string_next_divide
; no, do another division
39 os_int_to_string_next_digit:
40 pop rax
; else pop recent remainder
41 add al, '0' ; and convert to a numeral
42 stosb ; store to memory-buffer
43 loop os_int_to_string_next_digit
; again for other remainders
45 stosb ; Store the null terminator at the end of the string
52 ; -----------------------------------------------------------------------------
55 ; -----------------------------------------------------------------------------
56 ; os_string_to_int -- Convert a string into a binary interger
57 ; IN: RSI = location of string
58 ; OUT: RAX = integer value
59 ; All other registers preserved
60 ; Adapted from http://www.cs.usfca.edu/~cruse/cs210s09/uint2rax.s
67 xor eax, eax ; initialize accumulator
68 mov rbx
, 10 ; decimal-system's radix
69 os_string_to_int_next_digit:
70 mov cl, [rsi
] ; fetch next character
71 cmp cl, '0' ; char preceeds '0'?
72 jb os_string_to_int_invalid
; yes, not a numeral
73 cmp cl, '9' ; char follows '9'?
74 ja os_string_to_int_invalid
; yes, not a numeral
75 mul rbx
; ten times prior sum
76 and rcx
, 0x0F ; convert char to int
77 add rax
, rcx
; add to prior total
78 inc rsi
; advance source index
79 jmp os_string_to_int_next_digit
; and check another char
81 os_string_to_int_invalid:
87 ; -----------------------------------------------------------------------------
90 ; -----------------------------------------------------------------------------
91 ; os_int_to_hex_string -- Convert an integer to a hex string
92 ; IN: RAX = Integer value
93 ; RDI = location to store string
94 ; OUT: All registers preserved
102 mov rcx
, 16 ; number of nibbles. 64 bit = 16 nibbles = 8 bytes
103 os_int_to_hex_string_next_nibble:
104 rol rax
, 4 ; next nibble into AL
105 mov bl, al ; copy nibble into BL
106 and rbx
, 0x0F ; and convert to word
107 mov dl, [hextable
+ rbx
] ; lookup ascii numeral
112 loop os_int_to_hex_string_next_nibble
; again for next nibble
113 xor eax, eax ; clear RAX to 0
114 stosb ; Store AL to terminate string
122 ; -----------------------------------------------------------------------------
125 ; -----------------------------------------------------------------------------
126 ; os_hex_string_to_int -- Convert up to 8 hexascii to bin
127 ; IN: RSI = Location of hex asciiz string
128 ; OUT: RAX = binary value of hex string
129 ; All other registers preserved
130 os_hex_string_to_int:
137 os_hex_string_to_int_loop:
141 jb os_hex_string_to_int_ok
142 sub al, 0x20 ; convert to upper case if alpha
143 os_hex_string_to_int_ok:
144 sub al, '0' ; check if legal
145 jc os_hex_string_to_int_exit
; jmp if out of range
147 jle os_hex_string_to_int_got
; jmp if number is 0-9
148 sub al, 7 ; convert to number from A-F or 10-15
149 cmp al, 15 ; check if legal
150 ja os_hex_string_to_int_exit
; jmp if illegal hex char
151 os_hex_string_to_int_got:
154 jmp os_hex_string_to_int_loop
155 os_hex_string_to_int_exit:
156 mov rax
, rbx
; int value stored in RBX, move to RAX
162 ; -----------------------------------------------------------------------------
165 ; -----------------------------------------------------------------------------
166 ; os_string_length -- Return length of a string
167 ; IN: RSI = string location
168 ; OUT: RCX = length (not including the NULL terminator)
169 ; All other registers preserved
179 repne scasb ; compare byte at RDI to value in AL
186 ; -----------------------------------------------------------------------------
189 ; -----------------------------------------------------------------------------
190 ; os_string_find_char -- Find first location of character in a string
191 ; IN: RSI = string location
192 ; AL = character to find
193 ; OUT: RAX = location in string, or 0 if char not present
194 ; All other registers preserved
199 mov rcx
, 1 ; Counter -- start at first char
200 os_string_find_char_more:
202 je os_string_find_char_done
204 je os_string_find_char_not_found
207 jmp os_string_find_char_more
209 os_string_find_char_done:
216 os_string_find_char_not_found:
219 xor eax, eax ; not found, set RAX to 0
221 ; -----------------------------------------------------------------------------
224 ; -----------------------------------------------------------------------------
225 ; os_string_change_char -- Change all instances of a character in a string
226 ; IN: RSI = string location
227 ; AL = character to replace
228 ; BL = replacement character
229 ; OUT: All registers preserved
230 os_string_change_char:
237 os_string_change_char_loop:
240 je os_string_change_char_done
242 jne os_string_change_char_no_change
245 os_string_change_char_no_change:
247 jmp os_string_change_char_loop
249 os_string_change_char_done:
255 ; -----------------------------------------------------------------------------
258 ; -----------------------------------------------------------------------------
259 ; os_string_copy -- Copy the contents of one string into another
262 ; OUT: All registers preserved
263 ; Note: It is up to the programmer to ensure that there is sufficient space in the destination
270 lodsb ; Load a character from the source string
272 cmp al, 0 ; If source string is empty, quit out
273 jne os_string_copy_more
279 ; -----------------------------------------------------------------------------
282 ; -----------------------------------------------------------------------------
283 ; os_string_truncate -- Chop string down to specified number of characters
284 ; IN: RSI = string location
285 ; RAX = number of characters
286 ; OUT: All registers preserved
295 ; -----------------------------------------------------------------------------
298 ; -----------------------------------------------------------------------------
299 ; os_string_join -- Join two strings into a third string
300 ; IN: RAX = string one
302 ; RDI = destination string
303 ; OUT: All registers preserved
304 ; Note: It is up to the programmer to ensure that there is sufficient space in the destination
312 mov rsi
, rax
; Copy first string to location in RDI
314 call os_string_length
; Get length of first string
315 add rdi
, rcx
; Position at end of first string
316 mov rsi
, rbx
; Add second string onto it
325 ; -----------------------------------------------------------------------------
328 ; -----------------------------------------------------------------------------
329 ; os_string_append -- Append a string to an existing string
330 ; IN: RSI = String to be appended
331 ; RDI = Destination string
332 ; OUT: All registers preserved
333 ; Note: It is up to the programmer to ensure that there is sufficient space in the destination
340 call os_string_length
349 ; -----------------------------------------------------------------------------
352 ; -----------------------------------------------------------------------------
353 ; os_string_chomp -- Strip leading and trailing spaces from a string
354 ; IN: RSI = string location
355 ; OUT: All registers preserved
362 call os_string_length
; Quick check to see if there are any characters in the string
363 jrcxz os_string_chomp_done
; No need to work on it if there is no data
365 mov rdi
, rsi
; RDI will point to the start of the string...
366 push rdi
; ...while RSI will point to the "actual" start (without the spaces)
367 add rdi
, rcx
; os_string_length stored the length in RCX
369 os_string_chomp_findend: ; we start at the end of the string and move backwards until we don't find a space
371 cmp rsi
, rdi
; Check to make sure we are not reading backward past the string start
372 jg os_string_chomp_fail
; If so then fail (string only contained spaces)
374 je os_string_chomp_findend
376 inc rdi
; we found the real end of the string so null terminate it
380 os_string_chomp_start_count: ; read through string until we find a non-space character
382 jne os_string_chomp_copy
384 jmp os_string_chomp_start_count
386 os_string_chomp_fail: ; In this situataion the string is all spaces
387 pop rdi
; We are about to bail out so make sure the stack is sane
390 jmp os_string_chomp_done
392 ; At this point RSI points to the actual start of the string (minus the leading spaces, if any)
393 ; And RDI point to the start of the string
395 os_string_chomp_copy: ; Copy a byte from RSI to RDI one byte at a time until we find a NULL
399 jne os_string_chomp_copy
401 os_string_chomp_done:
407 ; -----------------------------------------------------------------------------
410 ; -----------------------------------------------------------------------------
411 ; os_string_strip -- Removes specified character from a string
412 ; IN: RSI = string location
413 ; AL = character to remove
414 ; OUT: All registers preserved
422 mov bl, al ; copy the char into BL since LODSB and STOSB use AL
423 os_string_strip_nextchar:
426 cmp al, 0x00 ; check if we reached the end of the string
427 je os_string_strip_done
; if so bail out
428 cmp al, bl ; check to see if the character we read is the interesting char
429 jne os_string_strip_nextchar
; if not skip to the next character
431 os_string_strip_skip: ; if so the fall through to here
432 dec rdi
; decrement RDI so we overwrite on the next pass
433 jmp os_string_strip_nextchar
435 os_string_strip_done:
441 ; -----------------------------------------------------------------------------
444 ; -----------------------------------------------------------------------------
445 ; os_string_compare -- See if two strings match
446 ; IN: RSI = string one
448 ; OUT: Carry flag set if same
455 os_string_compare_more:
456 mov al, [rsi
] ; Store string contents
458 cmp al, 0 ; End of first string?
459 je os_string_compare_terminated
461 jne os_string_compare_not_same
464 jmp os_string_compare_more
466 os_string_compare_not_same:
474 os_string_compare_terminated:
475 cmp bl, 0 ; End of second string?
476 jne os_string_compare_not_same
484 ; -----------------------------------------------------------------------------
487 ; -----------------------------------------------------------------------------
488 ; os_string_uppercase -- Convert zero-terminated string to uppercase
489 ; IN: RSI = string location
490 ; OUT: All registers preserved
494 os_string_uppercase_more:
495 cmp byte [rsi
], 0x00 ; Zero-termination of string?
496 je os_string_uppercase_done
; If so, quit
497 cmp byte [rsi
], 97 ; In the uppercase A to Z range?
498 jl os_string_uppercase_noatoz
500 jg os_string_uppercase_noatoz
501 sub byte [rsi
], 0x20 ; If so, convert input char to uppercase
503 jmp os_string_uppercase_more
505 os_string_uppercase_noatoz:
507 jmp os_string_uppercase_more
509 os_string_uppercase_done:
512 ; -----------------------------------------------------------------------------
515 ; -----------------------------------------------------------------------------
516 ; os_string_lowercase -- Convert zero-terminated string to lowercase
517 ; IN: RSI = string location
518 ; OUT: All registers preserved
522 os_string_lowercase_more:
523 cmp byte [rsi
], 0x00 ; Zero-termination of string?
524 je os_string_lowercase_done
; If so, quit
525 cmp byte [rsi
], 65 ; In the lowercase A to Z range?
526 jl os_string_lowercase_noatoz
528 jg os_string_lowercase_noatoz
529 add byte [rsi
], 0x20 ; If so, convert input char to lowercase
531 jmp os_string_lowercase_more
533 os_string_lowercase_noatoz:
535 jmp os_string_lowercase_more
537 os_string_lowercase_done:
540 ; -----------------------------------------------------------------------------
543 ; -----------------------------------------------------------------------------
544 ; os_get_time_string -- Store the current time in a string in format "HH:MM:SS"
545 ; IN: RDI = location to store string (must be able to fit 9 bytes, 8 data plus null terminator)
546 ; OUT: All registers preserved
552 os_get_time_string_wait:
556 test al, 0x80 ; Is there an update in progress?
557 jne os_get_time_string_wait
; If so then try again
563 jg os_get_time_string_lead_hours
565 os_get_time_string_lead_hours:
566 call os_int_to_string
570 mov al, 0x02 ; Minutes
575 jg os_get_time_string_lead_minutes
577 os_get_time_string_lead_minutes:
578 call os_int_to_string
582 mov al, 0x00 ; Seconds
587 jg os_get_time_string_lead_seconds
589 os_get_time_string_lead_seconds:
590 call os_int_to_string
604 ; -----------------------------------------------------------------------------
607 ; -----------------------------------------------------------------------------
608 ; os_get_date_string -- Store the current time in a string in format "YYYY/MM/DD"
609 ; IN: RDI = location to store string (must be able to fit 11 bytes, 10 data plus null terminator)
610 ; OUT: All registers preserved
611 ; Note: Uses the os_get_time_string_processor function
617 os_get_date_string_wait:
621 test al, 0x80 ; Is there an update in progress?
622 jne os_get_date_string_wait
; If so then try again
623 ; mov al, 0x32 ; Century
628 call os_int_to_string
634 call os_int_to_string
642 call os_int_to_string
650 call os_int_to_string
652 ; mov al, 0x00 ; Terminate the string
659 ; -----------------------------------------------------------------------------
662 ; -----------------------------------------------------------------------------
663 ; os_is_digit -- Check if character is a digit
664 ; IN: AL = ASCII char
665 ; OUT: EQ flag set if numeric
666 ; Note: JE (Jump if Equal) can be used after this function is called
669 jb os_is_digit_not_digit
671 ja os_is_digit_not_digit
672 cmp al, al ; To set the equal flag
674 os_is_digit_not_digit:
676 ; -----------------------------------------------------------------------------
679 ; -----------------------------------------------------------------------------
680 ; os_is_alpha -- Check if character is a letter
681 ; IN: AL = ASCII char
682 ; OUT: EQ flag set if alpha
683 ; Note: JE (Jump if Equal) can be used after this function is called
686 jb os_is_alpha_not_alpha
688 ja os_is_alpha_not_alpha
689 cmp al, al ; To set the equal flag
691 os_is_alpha_not_alpha:
693 ; -----------------------------------------------------------------------------
696 ; -----------------------------------------------------------------------------
697 ; os_string_parse -- Parse a string into individual words
698 ; IN: RSI = Address of string
699 ; OUT: RCX = word count
700 ; Note: This function will remove "extra" whitespace in the source string
701 ; "This is a test. " will update to "This is a test."
707 xor ecx, ecx ; RCX is our word counter
710 call os_string_chomp
; Remove leading and trailing spaces
712 cmp byte [rsi
], 0x00 ; Check the first byte
713 je os_string_parse_done
; If it is a null then bail out
714 inc rcx
; At this point we know we have at least one word
716 os_string_parse_next_char:
719 cmp al, 0x00 ; Check if we are at the end
720 je os_string_parse_done
; If so then bail out
721 cmp al, ' ' ; Is it a space?
722 je os_string_parse_found_a_space
723 jmp os_string_parse_next_char
; If not then grab the next char
725 os_string_parse_found_a_space:
726 lodsb ; We found a space.. grab the next char
727 cmp al, ' ' ; Is it a space as well?
728 jne os_string_parse_no_more_spaces
729 jmp os_string_parse_found_a_space
731 os_string_parse_no_more_spaces:
732 dec rsi
; Decrement so the next lodsb will read in the non-space
734 jmp os_string_parse_next_char
736 os_string_parse_done:
741 ; -----------------------------------------------------------------------------
744 ; =============================================================================