Created
September 24, 2014 09:49
-
-
Save anonymous/1ea19a39644f2031fb82 to your computer and use it in GitHub Desktop.
r/dailyprogrammer Challenge #180
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[9/17/2014] Challenge #180 [Intermediate] Tamagotchi emulator | |
http://www.reddit.com/r/dailyprogrammer/comments/2gryun/ | |
Only for windows x64 | |
compile this with: | |
nasm -f win64 t_main.asm | |
nasm -f win64 t_pet.asm | |
nasm -f win64 t_random.asm | |
gcc *.obj -o t.exe | |
- romcgb |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;;; ---------------------------------------------------------- | |
;;; NASM X86_64 Assembly tamagotchi for r/dailyprogrammer | |
;;; windows x64 | |
;;; romcgb | |
;;; | |
;;; [9/17/2014] Challenge #180 [Intermediate] Tamagotchi emulator | |
;;; http://www.reddit.com/r/dailyprogrammer/comments/2gryun/ | |
;;; ---------------------------------------------------------- | |
;;; Imports | |
;; Microsoft CRT | |
EXTERN puts | |
EXTERN printf | |
EXTERN __main | |
EXTERN scanf | |
EXTERN strcmp | |
EXTERN _kbhit | |
EXTERN getch | |
EXTERN fflush | |
EXTERN _iob | |
EXTERN time | |
;; windows.h | |
EXTERN Sleep | |
;; t_random.asm | |
EXTERN Random_init | |
;; t_pet.asm | |
EXTERN Pet_init | |
EXTERN Pet_update | |
EXTERN Pet_print_status | |
EXTERN Pet_shoot | |
EXTERN Pet_feed | |
EXTERN Pet_is_alive | |
EXTERN Pet_name | |
EXTERN Pet_clean | |
EXTERN Pet_sleep | |
;; Exports | |
GLOBAL main | |
SECTION .RODATA | |
hello_string db "Hello!", 10, 10 | |
db "Type 'help' to show help.", 10, 10 | |
db 0 | |
bye_string db "Bye!", 0 | |
age_fstring db "Pet age: %I64u", 10, 0 | |
scan_fstring db "%25s", 0 | |
cmd_fstring db "(PAUSED) > ", 0 | |
dead_fstring db "%s is dead!", 10, 0 | |
exit_string db "Press any key to exit.", 0 | |
cmd_helpstring db "help", 0 | |
cmd_feedstring db "feed", 0 | |
cmd_statusstring db "status", 0 | |
cmd_shootstring db "shoot", 0 | |
cmd_cleanstring db "clean", 0 | |
cmd_sleepstring db "sleep", 0 | |
cmd_exitstring db "exit", 0 | |
help_string db "Help:",10 | |
db " help: This help message.",10 | |
db " feed: Makes the pet eat some food.",10 | |
db " status: Prints the pet's status.",10 | |
db " shoot: Russian roulette.",10 | |
db " exit: Ends the program.",10 | |
db " clean: Cleans pet's room.",10 | |
db 0 | |
stdin equ _iob | |
update_fstring db "%c", 8, 0 | |
update_char db "-\|/" | |
SECTION .bss | |
command resb 26 ; buffer for command string (scanf) | |
running resb 1 ; main loop boolean | |
update_char_index resb 1 ; | |
SECTION .text | |
;;; -------------- | |
;;; ENTRY POINT | |
;;; -------------- | |
main: | |
ENTER 32, 0 | |
;; initializes MS CRT. | |
call __main | |
;; initializes random with seed. | |
XOR RCX, RCX | |
CALL time | |
MOV RCX, RAX | |
CALL Random_init | |
MOV RCX, hello_string | |
CALL puts | |
;; initializes the pet. | |
CALL Pet_init | |
MOV byte [running], 1 | |
.loop: | |
;; put update char | |
MOVZX RAX, byte [update_char_index] | |
MOV RCX, update_fstring | |
MOVZX RDX, byte [update_char+RAX] | |
INC AL | |
AND AL, 3 | |
MOV byte [update_char_index], AL | |
CALL printf | |
;; updates the pet. | |
CALL Pet_update | |
;; checks if user has pressed a key. | |
CALL _kbhit | |
CMP RAX, 0 | |
JE .no_command | |
;; clears the pressed key. | |
;CALL getch | |
;; opens command prompt. | |
MOV RCX, cmd_fstring | |
CALL printf | |
MOV RDX, command | |
MOV RCX, scan_fstring | |
CALL scanf | |
;; flush stdin. | |
MOV RCX, stdin | |
CALL fflush | |
;; handles typed command. | |
MOV R12, command | |
CALL handle_command | |
.no_command: | |
;; checks if pet is dead. | |
CALL Pet_is_alive | |
CMP RAX, 0 | |
JNE .continue | |
MOV byte [running], 0 | |
MOV RDX, Pet_name | |
MOV RCX, dead_fstring | |
CALL printf | |
.continue: | |
;; unload Cpu. | |
MOV RCX, 500 | |
CALL Sleep | |
;; looping ? | |
CMP byte [running], 0 | |
JNE .loop | |
.leave: | |
MOV RCX, bye_string | |
CALL puts | |
MOV RCX, exit_string | |
CALL puts | |
CALL getch | |
LEAVE | |
RET | |
;;; --------------------------------------------------------------------------- | |
;;; handle_command ( char* cmd:R12 ) -> None | |
;;; first arg in R12 because R12 is nonvolatile. this way, we can do multiple | |
;;; calls to strcmp without | |
;;; --------------------------------------------------------------------------- | |
handle_command: | |
ENTER 32, 0 | |
;; "help" ? | |
MOV RCX, R12 | |
MOV RDX, cmd_helpstring | |
CALL strcmp | |
CMP RAX, 0 | |
JNE .feed | |
CALL command_help | |
JMP .leave | |
.feed: | |
;; "feed" ? | |
MOV RCX, R12 | |
MOV RDX, cmd_feedstring | |
CALL strcmp | |
CMP RAX, 0 | |
JNE .status | |
CALL Pet_feed | |
JMP .leave | |
.status: | |
;; "status" ? | |
MOV RCX, R12 | |
MOV RDX, cmd_statusstring | |
CALL strcmp | |
CMP RAX, 0 | |
JNE .shoot | |
CALL Pet_print_status | |
JMP .leave | |
.shoot: | |
;; "shoot" ? | |
MOV RCX, R12 | |
MOV RDX, cmd_shootstring | |
CALL strcmp | |
CMP RAX, 0 | |
JNE .clean | |
CALL Pet_shoot | |
JMP .leave | |
.clean: | |
;; "clean" ? | |
MOV RCX, R12 | |
MOV RDX, cmd_cleanstring | |
CALL strcmp | |
CMP RAX, 0 | |
JNE .sleep | |
CALL Pet_clean | |
JMP .leave | |
.sleep: | |
;; "sleep" ? | |
MOV RCX, R12 | |
MOV RDX, cmd_sleepstring | |
CALL strcmp | |
CMP RAX, 0 | |
JNE .exit | |
CALL Pet_sleep | |
JMP .leave | |
.exit: | |
;; "exit" ? | |
MOV RCX, R12 | |
MOV RDX, cmd_exitstring | |
CALL strcmp | |
CMP RAX, 0 | |
JNE .leave | |
MOV byte [running], 0 | |
.leave: | |
LEAVE | |
RET | |
;;; --------------------------------------------------------------------------- | |
;;; command_help ( ) -> None | |
;;; Prints help | |
;;; --------------------------------------------------------------------------- | |
command_help: | |
ENTER 32, 0 | |
MOV RCX, help_string | |
CALL puts | |
LEAVE | |
RET |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;;; implements the pet behavior | |
;;; romcgb | |
;;; Imports | |
;; Microsoft CRT | |
EXTERN scanf | |
EXTERN printf | |
EXTERN puts | |
EXTERN time | |
EXTERN fflush | |
EXTERN _iob | |
;; t_random.asm | |
EXTERN Random_next | |
;;; Exports | |
GLOBAL Pet_name | |
GLOBAL Pet_init | |
GLOBAL Pet_update | |
GLOBAL Pet_print_status | |
GLOBAL Pet_is_alive | |
GLOBAL Pet_shoot | |
GLOBAL Pet_feed | |
GlOBAL Pet_clean | |
GLOBAL Pet_sleep | |
SECTION .RODATA | |
stdin equ _iob | |
HP_MAX equ 100 | |
FP_MAX equ 100 | |
STATUS_AWAKE equ 0 | |
STATUS_SLEEPING equ 1 | |
STATUS_DEAD equ 2 | |
COND_NONE equ 0 | |
COND_HUNGRY equ 1 | |
COND_TIRED equ 2 | |
PRINT_FSTRING db "Enter the name of the pet: ", 0 | |
PRINT_FSTRING_2 db "%s has just been born.",10,0 | |
SCAN_FSTRING db "%25s", 0 | |
NATDEATH_STRING db "%s is having a heart attack!",10,0 | |
HPDEATH_STRING db "%s died of hunger.",10,0 | |
SHOOT_FSTRING db "%s plays some russian roulette: ", 0 | |
SHOOT_ALSTRING db "Click!", 0 | |
SHOOT_DESTRING db "Clack!", 0 | |
FP_70_STRING db "%s is hungry.",10,0 | |
FP_50_STRING db "%s is really hungry.",10,0 | |
FP_20_STRING db "%s is very hungry.",10,0 | |
FP_0_STRING db "%s is losing life cause of hunger.",10,0 | |
POOP_FSTRING db "%s has poop!",10,0 | |
POOP_CLEAN_STRING db "You have cleaned %d poops.",10,0 | |
FEED_STRING db "You have fed %s.",10,0 | |
TEST_STRING db "%llu %llu",10,0 | |
STATUS_STRING1 db "Pet status:", 10, 0 | |
STATUS_STRING2 db " Age: %d", 10 | |
db " Poops: %d", 10 | |
db " Status: %s", 10,0 | |
STATUS_STRING3 db " Condition: %s", 10, 0 | |
SLEEPING_STRING db "Sleeping", 0 | |
AWAKE_STRING db "Awake", 0 | |
GSLEEP_STRING db "%s is tired and now sleeping.", 10, 0 | |
MSLEEP_STRING db "You put %s to sleep.", 10, 0 | |
ASLEEP_STRING db "%s is no more sleeping.", 10, 0 | |
WSLEEP_STRING db "%s doesn't want to sleep.", 10, 0 | |
ESLEEP_STRING db "%s is already sleeping!.", 10, 0 | |
SECTION .data | |
Pet_birth_time dq 0 ; | |
Pet_death_time dq 0 ; Pet_birth_time+(600..1200) | |
Pet_age dq 0 ; | |
Pet_hp db HP_MAX ; 0..100 health points | |
Pet_fp db FP_MAX ; 0..100 feed points | |
Pet_status db STATUS_AWAKE ; {Awake|Sleeping|DEAD} | |
Pet_condition db COND_NONE ; {None|Hungry} | |
Pet_poop_counter db 0 | |
Pet_poop_timer dq 0 ; 50..200 | |
Pet_fp_timer dq 0 | |
Pet_as_timer dq 0 | |
previous_time dq 0 | |
SECTION .bss | |
Pet_name resb 26 ; 25 chars + '\0' | |
SECTION .text | |
;;; --------------------------------------------------------------------------- | |
;;; Pet_init ( ) -> None | |
;;; Initializes the pet | |
;;; --------------------------------------------------------------------------- | |
Pet_init: | |
ENTER 32, 0 | |
;; asks the name of the pet | |
MOV RCX, PRINT_FSTRING | |
CALL printf | |
MOV RCX, SCAN_FSTRING | |
MOV RDX, Pet_name | |
CALL scanf | |
MOV RCX, PRINT_FSTRING_2 | |
MOV RDX, Pet_name | |
CALL printf | |
MOV RCX, stdin | |
CALL fflush | |
;; sets birth time | |
MOV RCX, Pet_birth_time | |
CALL time | |
MOV R10, RAX ; we keep current time in R10 | |
;; sets death time | |
CALL Random_next | |
XOR RDX, RDX | |
MOV RCX, 600 | |
DIV RCX | |
LEA RAX, [RDX+R10+600] | |
MOV [Pet_death_time], RAX | |
;; sets poop counter | |
CALL Random_next | |
XOR RDX, RDX | |
MOV RCX, 150 | |
DIV RCX | |
LEA RAX, [RDX+R10+50] | |
MOV [Pet_poop_timer], RAX | |
;; sets fp timer | |
CALL Random_next | |
XOR RDX, RDX | |
MOV RCX, 3 | |
DIV RCX | |
LEA RAX, [RDX+R10+3] | |
MOV [Pet_fp_timer], RAX | |
;; set sleep timer | |
CALL Random_next | |
XOR RDX, RDX | |
MOV RCX, 40 | |
DIV RCX | |
LEA RAX, [RDX+R10+20] | |
MOV [Pet_as_timer], RAX | |
MOV [previous_time], R10 | |
.leave: | |
LEAVE | |
RET | |
;;; --------------------------------------------------------------------------- | |
;;; Pet_update ( ) -> None | |
;;; Updates pet's status | |
;;; --------------------------------------------------------------------------- | |
Pet_update: | |
ENTER 32, 0 | |
;; updates pet_age: | |
;; pet_age := current_time - pet_birth_time | |
XOR RCX, RCX | |
CALL time | |
MOV R12, RAX ; we keep current time in R12 | |
SUB RAX, [Pet_birth_time] | |
MOV [Pet_age], RAX | |
CMP [Pet_as_timer], R12 | |
JA .no_sleep_awake | |
CALL Random_next | |
CMP byte [Pet_status], STATUS_SLEEPING | |
JNE .not_sleeping | |
MOV RCX, 100 | |
XOR RDX, RDX | |
DIV RCX | |
LEA RAX, [RDX+R12+50] | |
MOV [Pet_as_timer], RAX | |
MOV byte [Pet_status], STATUS_AWAKE | |
MOV RCX, ASLEEP_STRING | |
MOV RDX, Pet_name | |
CALL printf | |
JMP .poop_timer | |
.not_sleeping: | |
MOV RCX, 40 | |
XOR RDX, RDX | |
DIV RCX | |
LEA RAX, [RDX+R12+20] | |
MOV [Pet_as_timer], RAX | |
MOV byte [Pet_status], STATUS_SLEEPING | |
MOV RCX, GSLEEP_STRING | |
MOV RDX, Pet_name | |
CALL printf | |
JMP .leave | |
.no_sleep_awake: | |
CMP byte [Pet_status], STATUS_SLEEPING | |
JE .leave | |
.poop_timer: | |
;; is time above poop timer ? | |
CMP [Pet_poop_timer], R12 | |
JA .no_poop | |
;; inc poop counter and set poop timer | |
INC byte [Pet_poop_counter] | |
CALL Random_next | |
MOV RCX, 150 | |
XOR RDX, RDX | |
DIV RCX | |
LEA RAX, [RDX+R12+50] | |
MOV [Pet_poop_timer], RAX | |
MOV RCX, POOP_FSTRING | |
MOV RDX, Pet_name | |
CALL printf | |
.no_poop: | |
;; is time above death time ? | |
CMP [Pet_death_time], R12 | |
JA .no_death | |
MOV RCX, NATDEATH_STRING | |
MOV RDX, Pet_name | |
CALL printf | |
JMP .set_dead | |
.no_death: | |
;; is time above fp timer ? | |
CMP [Pet_fp_timer], R12 | |
JA .leave | |
CALL Random_next | |
MOV RCX, 3 | |
XOR RDX, RDX | |
DIV RCX | |
LEA RAX, [RDX+R12+3] | |
MOV [Pet_fp_timer], RAX | |
MOVZX R13, byte [Pet_fp] | |
;; pet_fp equals 0 ? | |
CMP R13, 0 | |
JNE .check_70_fp | |
MOV RCX, FP_0_STRING | |
MOV RDX, Pet_name | |
CALL printf | |
DEC byte [Pet_hp] | |
;; pet_hp equals 0 ? | |
CMP byte [Pet_hp], 0 | |
JNE .leave | |
MOV RCX, HPDEATH_STRING | |
MOV RDX, Pet_name | |
CALL printf | |
JMP .set_dead | |
.check_70_fp: | |
DEC byte [Pet_fp] | |
CMP R13, 70 | |
JNE .check_50_fp | |
MOV RCX, FP_70_STRING | |
MOV RDX, Pet_name | |
CALL printf | |
JMP .leave | |
.check_50_fp: | |
CMP R13, 50 | |
JNE .check_20_fp | |
MOV RCX, FP_50_STRING | |
MOV RDX, Pet_name | |
CALL printf | |
JMP .leave | |
.check_20_fp: | |
CMP R13, 20 | |
JNE .leave | |
MOV RCX, FP_20_STRING | |
MOV RDX, Pet_name | |
CALL printf | |
JMP .leave | |
.set_dead: | |
;; sets status to death | |
MOV byte [Pet_status], STATUS_DEAD | |
.leave: | |
LEAVE | |
RET | |
;;; --------------------------------------------------------------------------- | |
;;; Pet_feed ( ) -> None | |
;;; Feeds the pet (+30 FP) | |
;;; --------------------------------------------------------------------------- | |
Pet_feed: | |
ENTER 32, 0 | |
MOVZX RAX, byte [Pet_fp] | |
ADD AL, 30 | |
CMP AL, FP_MAX ; checks if Pet_fp > FP_MAX | |
JNA .no_overflow | |
MOV RAX, FP_MAX | |
.no_overflow: | |
MOV byte [Pet_fp], AL | |
MOV RCX, FEED_STRING | |
MOV RDX, Pet_name | |
CALL printf | |
LEAVE | |
RET | |
;;; --------------------------------------------------------------------------- | |
;;; Pet_shoot ( ) -> None | |
;;; Russian roulette | |
;;; --------------------------------------------------------------------------- | |
Pet_shoot: | |
ENTER 32, 0 | |
MOV RCX, SHOOT_FSTRING | |
MOV RDX, Pet_name | |
CALL printf | |
CALL Random_next | |
XOR RDX, RDX | |
MOV RCX, 5 | |
DIV RCX | |
CMP RDX, 2 | |
JNE .alive | |
MOV byte [Pet_status], STATUS_DEAD | |
MOV RCX, SHOOT_DESTRING | |
CALL puts | |
JMP .leave | |
.alive: | |
MOV RCX, SHOOT_ALSTRING | |
CALL puts | |
.leave: | |
LEAVE | |
RET | |
;;; --------------------------------------------------------------------------- | |
;;; Pet_is_alive ( ) -> bool:RAX | |
;;; Checks if pet is alive | |
;;; --------------------------------------------------------------------------- | |
Pet_is_alive: | |
XOR RAX, RAX | |
CMP byte [Pet_status], STATUS_DEAD | |
SETNE AL | |
RET | |
;;; --------------------------------------------------------------------------- | |
;;; Pet_print_status ( ) -> None | |
;;; Prints pet's informations | |
;;; --------------------------------------------------------------------------- | |
Pet_print_status: | |
ENTER 32, 0 | |
MOV RCX, STATUS_STRING1 | |
CALL printf | |
MOV RCX, STATUS_STRING2 | |
MOVZX RDX, byte [Pet_age] | |
MOVZX R8, byte [Pet_poop_counter] | |
MOVZX R10, byte [Pet_status] | |
CMP R10, STATUS_AWAKE | |
JNE .is_sleeping | |
MOV R9, AWAKE_STRING | |
JMP .print | |
.is_sleeping: | |
MOV R9, SLEEPING_STRING | |
.print: | |
CALL printf | |
LEAVE | |
RET | |
;;; --------------------------------------------------------------------------- | |
;;; Pet_print_status ( ) -> None | |
;;; Prints pet's informations | |
;;; --------------------------------------------------------------------------- | |
Pet_clean: | |
ENTER 32, 0 | |
MOVZX RDX, byte [Pet_poop_counter] | |
MOV RCX, POOP_CLEAN_STRING | |
CALL printf | |
MOV byte [Pet_poop_counter], 0 | |
LEAVE | |
RET | |
;;; --------------------------------------------------------------------------- | |
;;; Pet_sleep ( ) -> None | |
;;; Make the pet sleep | |
;;; --------------------------------------------------------------------------- | |
Pet_sleep: | |
ENTER 32, 0 | |
CMP byte [Pet_status], STATUS_SLEEPING | |
JE .already_sleeping | |
MOV byte [Pet_status], STATUS_SLEEPING | |
XOR RCX, RCX | |
CALL time | |
MOV R12, RAX | |
CALL Random_next | |
MOV RCX, 40 | |
XOR RDX, RDX | |
DIV RCX | |
LEA RAX, [RDX+R12+20] | |
MOV [Pet_as_timer], RAX | |
MOV RCX, MSLEEP_STRING | |
MOV RDX, Pet_name | |
JMP .leave | |
.already_sleeping: | |
MOV RCX, ESLEEP_STRING | |
MOV RDX, Pet_name | |
.leave: | |
CALL printf | |
LEAVE | |
RET |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;;; Implements xorshift64* | |
;;; http://vigna.di.unimi.it/ftp/papers/xorshift.pdf | |
;;; romcgb | |
;;; Exports | |
GLOBAL Random_init | |
GLOBAL Random_next | |
SECTION .bss | |
seed resq 1 | |
SECTION .text | |
;;; --------------------------------------------------------------------------- | |
;;; Random_init ( u64 new_seed:RCX ) -> None | |
;;; Sets the seed | |
;;; --------------------------------------------------------------------------- | |
Random_init: | |
MOV [seed], RCX | |
RET | |
;;; --------------------------------------------------------------------------- | |
;;; Random_next () -> u64:RAX | |
;;; Returns a pseudo randomized qwad word | |
;;; --------------------------------------------------------------------------- | |
Random_next: | |
MOV RCX, [seed] | |
MOV RDX, RCX | |
SHR RDX, 12 | |
XOR RCX, RDX | |
MOV RDX, RCX | |
SHL RDX, 25 | |
XOR RCX, RDX | |
MOV RDX, RCX | |
SHR RDX, 27 | |
XOR RCX, RDX | |
MOV RAX, 2685821657736338717 | |
MUL RCX | |
MOV [seed], RCX | |
RET |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment