Skip to content

Instantly share code, notes, and snippets.

Created September 24, 2014 09:49
Show Gist options
  • Save anonymous/1ea19a39644f2031fb82 to your computer and use it in GitHub Desktop.
Save anonymous/1ea19a39644f2031fb82 to your computer and use it in GitHub Desktop.
r/dailyprogrammer Challenge #180
[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
;;; ----------------------------------------------------------
;;; 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
;;; 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
;;; 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