* mikeOS 16 bit and amd64 baremetal
[mascara-docs.git] / amd64 / bareMetalOS-0.5.2 / baremetal0.5.2 / os / syscalls / string.asm
blobb1fa2e22bacedb044f27c421a4e6ce077d8ecc4d
1 ; =============================================================================
2 ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems
3 ; Copyright (C) 2008-2011 Return Infinity -- see LICENSE.TXT
5 ; String Functions
6 ; =============================================================================
8 align 16
9 db 'DEBUG: STRING '
10 align 16
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
23 os_int_to_string:
24 push rdx
25 push rcx
26 push rbx
27 push rax
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
44 xor al, al
45 stosb ; Store the null terminator at the end of the string
47 pop rax
48 pop rbx
49 pop rcx
50 pop rdx
51 ret
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
61 os_string_to_int:
62 push rsi
63 push rdx
64 push rcx
65 push rbx
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:
82 pop rbx
83 pop rcx
84 pop rdx
85 pop rsi
86 ret
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
95 os_int_to_hex_string:
96 push rdi
97 push rdx
98 push rcx
99 push rbx
100 push rax
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
108 push rax
109 mov al, dl
110 stosb
111 pop rax
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
116 pop rax
117 pop rbx
118 pop rcx
119 pop rdx
120 pop rdi
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:
131 push rsi
132 push rcx
133 push rbx
136 xor ebx, ebx
137 os_hex_string_to_int_loop:
138 lodsb
139 mov cl, 4
140 cmp al, 'a'
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
146 cmp al, 9
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:
152 shl rbx, cl
153 or bl, al
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
158 pop rbx
159 pop rcx
160 pop rsi
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
170 os_string_length:
171 push rdi
172 push rax
174 xor ecx, ecx
175 xor eax, eax
176 mov rdi, rsi
177 not rcx
179 repne scasb ; compare byte at RDI to value in AL
180 not rcx
181 dec rcx
183 pop rax
184 pop rdi
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
195 os_string_find_char:
196 push rsi
197 push rcx
199 mov rcx, 1 ; Counter -- start at first char
200 os_string_find_char_more:
201 cmp byte [rsi], al
202 je os_string_find_char_done
203 cmp byte [rsi], 0
204 je os_string_find_char_not_found
205 inc rsi
206 inc rcx
207 jmp os_string_find_char_more
209 os_string_find_char_done:
210 mov rax, rcx
212 pop rcx
213 pop rsi
216 os_string_find_char_not_found:
217 pop rcx
218 pop rsi
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:
231 push rsi
232 push rcx
233 push rbx
234 push rax
236 mov cl, al
237 os_string_change_char_loop:
238 mov byte al, [rsi]
239 cmp al, 0
240 je os_string_change_char_done
241 cmp al, cl
242 jne os_string_change_char_no_change
243 mov byte [rsi], bl
245 os_string_change_char_no_change:
246 inc rsi
247 jmp os_string_change_char_loop
249 os_string_change_char_done:
250 pop rax
251 pop rbx
252 pop rcx
253 pop rsi
255 ; -----------------------------------------------------------------------------
258 ; -----------------------------------------------------------------------------
259 ; os_string_copy -- Copy the contents of one string into another
260 ; IN: RSI = source
261 ; RDI = destination
262 ; OUT: All registers preserved
263 ; Note: It is up to the programmer to ensure that there is sufficient space in the destination
264 os_string_copy:
265 push rsi
266 push rdi
267 push rax
269 os_string_copy_more:
270 lodsb ; Load a character from the source string
271 stosb
272 cmp al, 0 ; If source string is empty, quit out
273 jne os_string_copy_more
275 pop rax
276 pop rdi
277 pop rsi
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
287 os_string_truncate:
288 push rsi
290 add rsi, rax
291 mov byte [rsi], 0x00
293 pop rsi
295 ; -----------------------------------------------------------------------------
298 ; -----------------------------------------------------------------------------
299 ; os_string_join -- Join two strings into a third string
300 ; IN: RAX = string one
301 ; RBX = string two
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
305 os_string_join:
306 push rsi
307 push rdi
308 push rcx
309 push rbx
310 push rax
312 mov rsi, rax ; Copy first string to location in RDI
313 call os_string_copy
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
317 call os_string_copy
319 pop rax
320 pop rbx
321 pop rcx
322 pop rdi
323 pop rsi
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
334 os_string_append:
335 push rsi
336 push rdi
337 push rcx
339 xchg rsi, rdi
340 call os_string_length
341 xchg rsi, rdi
342 add rdi, rcx
343 call os_string_copy
345 pop rcx
346 pop rdi
347 pop rsi
349 ; -----------------------------------------------------------------------------
352 ; -----------------------------------------------------------------------------
353 ; os_string_chomp -- Strip leading and trailing spaces from a string
354 ; IN: RSI = string location
355 ; OUT: All registers preserved
356 os_string_chomp:
357 push rsi
358 push rdi
359 push rcx
360 push rax
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
370 dec rdi
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)
373 cmp byte [rdi], ' '
374 je os_string_chomp_findend
376 inc rdi ; we found the real end of the string so null terminate it
377 mov byte [rdi], 0x00
378 pop rdi
380 os_string_chomp_start_count: ; read through string until we find a non-space character
381 cmp byte [rsi], ' '
382 jne os_string_chomp_copy
383 inc rsi
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
388 mov al, 0x00
389 stosb
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
396 lodsb
397 stosb
398 cmp al, 0x00
399 jne os_string_chomp_copy
401 os_string_chomp_done:
402 pop rax
403 pop rcx
404 pop rdi
405 pop rsi
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
415 os_string_strip:
416 push rsi
417 push rdi
418 push rbx
419 push rax
421 mov rdi, rsi
422 mov bl, al ; copy the char into BL since LODSB and STOSB use AL
423 os_string_strip_nextchar:
424 lodsb
425 stosb
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:
436 pop rax
437 pop rbx
438 pop rdi
439 pop rsi
441 ; -----------------------------------------------------------------------------
444 ; -----------------------------------------------------------------------------
445 ; os_string_compare -- See if two strings match
446 ; IN: RSI = string one
447 ; RDI = string two
448 ; OUT: Carry flag set if same
449 os_string_compare:
450 push rsi
451 push rdi
452 push rbx
453 push rax
455 os_string_compare_more:
456 mov al, [rsi] ; Store string contents
457 mov bl, [rdi]
458 cmp al, 0 ; End of first string?
459 je os_string_compare_terminated
460 cmp al, bl
461 jne os_string_compare_not_same
462 inc rsi
463 inc rdi
464 jmp os_string_compare_more
466 os_string_compare_not_same:
467 pop rax
468 pop rbx
469 pop rdi
470 pop rsi
474 os_string_compare_terminated:
475 cmp bl, 0 ; End of second string?
476 jne os_string_compare_not_same
478 pop rax
479 pop rbx
480 pop rdi
481 pop rsi
484 ; -----------------------------------------------------------------------------
487 ; -----------------------------------------------------------------------------
488 ; os_string_uppercase -- Convert zero-terminated string to uppercase
489 ; IN: RSI = string location
490 ; OUT: All registers preserved
491 os_string_uppercase:
492 push rsi
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
499 cmp byte [rsi], 122
500 jg os_string_uppercase_noatoz
501 sub byte [rsi], 0x20 ; If so, convert input char to uppercase
502 inc rsi
503 jmp os_string_uppercase_more
505 os_string_uppercase_noatoz:
506 inc rsi
507 jmp os_string_uppercase_more
509 os_string_uppercase_done:
510 pop rsi
512 ; -----------------------------------------------------------------------------
515 ; -----------------------------------------------------------------------------
516 ; os_string_lowercase -- Convert zero-terminated string to lowercase
517 ; IN: RSI = string location
518 ; OUT: All registers preserved
519 os_string_lowercase:
520 push rsi
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
527 cmp byte [rsi], 90
528 jg os_string_lowercase_noatoz
529 add byte [rsi], 0x20 ; If so, convert input char to lowercase
530 inc rsi
531 jmp os_string_lowercase_more
533 os_string_lowercase_noatoz:
534 inc rsi
535 jmp os_string_lowercase_more
537 os_string_lowercase_done:
538 pop rsi
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
547 os_get_time_string:
548 push rdi
549 push rbx
550 push rax
552 os_get_time_string_wait:
553 mov al, 10
554 out 0x70, al
555 in al, 0x71
556 test al, 0x80 ; Is there an update in progress?
557 jne os_get_time_string_wait ; If so then try again
558 mov al, 0x04 ; Hours
559 out 0x70, al
560 xor eax, eax
561 in al, 0x71
562 cmp al, 9
563 jg os_get_time_string_lead_hours
564 call os_leading_zero
565 os_get_time_string_lead_hours:
566 call os_int_to_string
567 sub rdi, 1
568 mov al, ':'
569 stosb
570 mov al, 0x02 ; Minutes
571 out 0x70, al
572 xor eax, eax
573 in al, 0x71
574 cmp al, 9
575 jg os_get_time_string_lead_minutes
576 call os_leading_zero
577 os_get_time_string_lead_minutes:
578 call os_int_to_string
579 sub rdi, 1
580 mov al, ':'
581 stosb
582 mov al, 0x00 ; Seconds
583 out 0x70, al
584 xor eax, eax
585 in al, 0x71
586 cmp al, 9
587 jg os_get_time_string_lead_seconds
588 call os_leading_zero
589 os_get_time_string_lead_seconds:
590 call os_int_to_string
591 stosb
593 pop rax
594 pop rbx
595 pop rdi
598 os_leading_zero:
599 push ax
600 mov al, '0'
601 stosb
602 pop ax
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
612 os_get_date_string:
613 push rdi
614 push rbx
615 push rax
617 os_get_date_string_wait:
618 mov al, 10
619 out 0x70, al
620 in al, 0x71
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
624 ; out 0x70, al
625 xor eax, eax
626 ; in al, 0x71
627 mov al, 20
628 call os_int_to_string
629 sub rdi, 1
630 mov al, 0x09 ; Year
631 out 0x70, al
632 xor eax, eax
633 in al, 0x71
634 call os_int_to_string
635 sub rdi, 1
636 mov al, '/'
637 stosb
638 mov al, 0x08 ; Month
639 out 0x70, al
640 xor eax, eax
641 in al, 0x71
642 call os_int_to_string
643 sub rdi, 1
644 mov al, '/'
645 stosb
646 mov al, 0x07 ; Day
647 out 0x70, al
648 xor eax, eax
649 in al, 0x71
650 call os_int_to_string
651 ; sub rdi, 1
652 ; mov al, 0x00 ; Terminate the string
653 stosb
655 pop rax
656 pop rbx
657 pop rdi
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
667 os_is_digit:
668 cmp al, '0'
669 jb os_is_digit_not_digit
670 cmp al, '9'
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
684 os_is_alpha:
685 cmp al, ' '
686 jb os_is_alpha_not_alpha
687 cmp al, 0x7E
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."
702 os_string_parse:
703 push rsi
704 push rdi
705 push rax
707 xor ecx, ecx ; RCX is our word counter
708 mov rdi, rsi
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:
717 lodsb
718 stosb
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
733 inc rcx
734 jmp os_string_parse_next_char
736 os_string_parse_done:
737 pop rax
738 pop rdi
739 pop rsi
741 ; -----------------------------------------------------------------------------
744 ; =============================================================================
745 ; EOF