sd:writing_games_in_assembly_language
Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| sd:writing_games_in_assembly_language [2026/05/10 10:33] – appledog | sd:writing_games_in_assembly_language [2026/05/10 16:36] (current) – appledog | ||
|---|---|---|---|
| Line 1076: | Line 1076: | ||
| CALL @draw_borders | CALL @draw_borders | ||
| CALL @draw_world | CALL @draw_world | ||
| - | CALL @draw_player | ||
| CALL @draw_mobs | CALL @draw_mobs | ||
| + | CALL @draw_player | ||
| CALL @draw_stats | CALL @draw_stats | ||
| CALL @draw_msgs | CALL @draw_msgs | ||
| Line 1662: | Line 1662: | ||
| JZ @draw_items_loop | JZ @draw_items_loop | ||
| + | LDI @OBJ_TYPE | ||
| + | LDA [ELM+I] | ||
| + | CMP A, #1 | ||
| + | JNZ @draw_items_loop | ||
| + | | ||
| ; Does this item have an X and Y position? | ; Does this item have an X and Y position? | ||
| LDI @OBJ_X | LDI @OBJ_X | ||
| Line 2112: | Line 2117: | ||
| CALL @draw_world | CALL @draw_world | ||
| ;CALL @draw_items | ;CALL @draw_items | ||
| - | ;CALL @draw_player | ||
| ;CALL @draw_mobs | ;CALL @draw_mobs | ||
| + | ;CALL @draw_player | ||
| CALL @draw_stats | CALL @draw_stats | ||
| CALL @draw_msgs | CALL @draw_msgs | ||
| Line 3281: | Line 3286: | ||
| PUSH B | PUSH B | ||
| - | ; --- Default: | + | ; map tile glyph |
| LDK [@map1_dim] | LDK [@map1_dim] | ||
| MOV Z, Y | MOV Z, Y | ||
| Line 3290: | Line 3295: | ||
| LDAL [ELM] ; AL = map glyph (default return) | LDAL [ELM] ; AL = map glyph (default return) | ||
| - | ; --- Override if any object sits at (X, Y) --- | + | ; Override if any object sits at (X, Y) |
| LDFLD @OBJS_BASE | LDFLD @OBJS_BASE | ||
| get_glyph_obj_loop: | get_glyph_obj_loop: | ||
| LDFLD [FLD] | LDFLD [FLD] | ||
| CMP FLD, @OBJS_BASE | CMP FLD, @OBJS_BASE | ||
| - | JZ @get_glyph_done | + | JZ @get_glyph_done |
| LDI @OBJ_ID | LDI @OBJ_ID | ||
| Line 3311: | Line 3316: | ||
| JNZ @get_glyph_obj_loop | JNZ @get_glyph_obj_loop | ||
| - | ; Match — override AL with the object' | + | ; Match will override AL with the object' |
| LDI @OBJ_VIS | LDI @OBJ_VIS | ||
| LDAL [FLD+I] | LDAL [FLD+I] | ||
| Line 3381: | Line 3386: | ||
| RET | RET | ||
| </ | </ | ||
| + | |||
| + | ==== move_mon | ||
| + | This is where we attempt to move the monsters. Don't forget to move them //before// you draw them in the game loop: | ||
| + | |||
| + | draw_map: | ||
| + | LDAH $51 ; VSTOP | ||
| + | INT 0x18 | ||
| + | |||
| + | CALL @draw_borders | ||
| + | CALL @draw_world | ||
| + | CALL @draw_items | ||
| + | CALL @move_mon | ||
| + | CALL @draw_mobs | ||
| + | CALL @draw_player | ||
| + | CALL @draw_stats | ||
| + | CALL @draw_msgs | ||
| + | CALL @msg_display | ||
| + | |||
| + | LDAH $50 ; VSTART | ||
| + | INT 0x18 | ||
| + | |||
| + | RET | ||
| + | |||
| + | Add this in, and add '' | ||
| + | |||
| + | <codify armasm> | ||
| + | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
| + | ;; move_mon | ||
| + | ;; | ||
| + | ;; Attempt to move every live monster | ||
| + | ;; towards the player | ||
| + | ;; | ||
| + | |||
| + | move_mon: | ||
| + | LDELM @OBJS_BASE | ||
| + | move_mon_loop: | ||
| + | LDELM [ELM] | ||
| + | CMP ELM, @OBJS_BASE | ||
| + | JZ @move_mon_done | ||
| + | |||
| + | ; live monsters only | ||
| + | LDI @OBJ_ID | ||
| + | LDA [ELM+I] | ||
| + | JZ @move_mon_loop | ||
| + | LDI @OBJ_TYPE | ||
| + | LDA [ELM+I] | ||
| + | CMP A, #2 ; OBJ_TYPE_MONSTER | ||
| + | JNZ @move_mon_loop | ||
| + | |||
| + | LDI @OBJ_X | ||
| + | LDXL [ELM+I] | ||
| + | LDI @OBJ_Y | ||
| + | LDYL [ELM+I] | ||
| + | |||
| + | ; sign(PX - mx) to CL | ||
| + | LDAL [@PX] | ||
| + | CMP AL, XL | ||
| + | JZ @mm_dx_zero | ||
| + | JC @mm_dx_pos | ||
| + | LDCL #$FF ; PX < mx, step left | ||
| + | JMP @mm_dx_done | ||
| + | mm_dx_pos: | ||
| + | LDCL #$01 | ||
| + | JMP @mm_dx_done | ||
| + | mm_dx_zero: | ||
| + | LDCL #$00 | ||
| + | mm_dx_done: | ||
| + | |||
| + | ; sign(PY - my) into DL | ||
| + | LDAL [@PY] | ||
| + | CMP AL, YL | ||
| + | JZ @mm_dy_zero | ||
| + | JC @mm_dy_pos | ||
| + | LDDL #$FF | ||
| + | JMP @mm_dy_done | ||
| + | mm_dy_pos: | ||
| + | LDDL #$01 | ||
| + | JMP @mm_dy_done | ||
| + | mm_dy_zero: | ||
| + | LDDL #$00 | ||
| + | mm_dy_done: | ||
| + | |||
| + | ; Pick axis | ||
| + | CMP CL, #0 | ||
| + | JNZ @mm_dx_nz | ||
| + | CMP DL, #0 | ||
| + | JZ @move_mon_loop | ||
| + | JMP @mm_use_y | ||
| + | |||
| + | mm_dx_nz: | ||
| + | CMP DL, #0 | ||
| + | JZ @mm_use_x | ||
| + | |||
| + | ; Both non-zero (coin flip) | ||
| + | LDAH $00 | ||
| + | INT 0x13 | ||
| + | MOD B, #2 | ||
| + | CMP B, #0 | ||
| + | JZ @mm_use_x | ||
| + | ; fall through to mm_use_y | ||
| + | |||
| + | mm_use_y: | ||
| + | ADD YL, DL ; YL = my + sign(dy) | ||
| + | JMP @mm_check | ||
| + | mm_use_x: | ||
| + | ADD XL, CL ; XL = mx + sign(dx) | ||
| + | |||
| + | mm_check: | ||
| + | ; Don't step on the player | ||
| + | LDAL [@PX] | ||
| + | CMP AL, XL | ||
| + | JNZ @mm_check_obj | ||
| + | LDAL [@PY] | ||
| + | CMP AL, YL | ||
| + | JZ @move_mon_loop | ||
| + | |||
| + | mm_check_obj: | ||
| + | PUSH ELM | ||
| + | CALL @has_mon | ||
| + | JC @mm_pop_skip | ||
| + | CALL @is_walkable | ||
| + | JNC @mm_pop_skip | ||
| + | |||
| + | ; commit the move | ||
| + | POP ELM | ||
| + | LDI @OBJ_X | ||
| + | STXL [ELM+I] | ||
| + | LDI @OBJ_Y | ||
| + | STYL [ELM+I] | ||
| + | JMP @move_mon_loop | ||
| + | |||
| + | mm_pop_skip: | ||
| + | POP ELM | ||
| + | JMP @move_mon_loop | ||
| + | |||
| + | move_mon_done: | ||
| + | RET | ||
| + | </ | ||
| + | |||
| + | === Combat | ||
| + | When a monster attempts to move onto a player, this is a combat situation. | ||
| + | |||
| + | When a player attempts to move onto a monster, this is also a combat situation. | ||
| + | |||
| + | Ideally we want to use the same code, but this will never work out in practice unless we add the player as an object in the item list (so it has data like a monster). This is actually one way of running the game, but for now we will have a special case to set up player vs monster or monster vs player. What we will do is set up a general data structure and have the code for player/ | ||
| + | |||
| + | Let's change mon_move to call the combat function: | ||
| + | |||
| + | <codify armasm> | ||
| + | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
| + | ;; move_mon | ||
| + | ;; | ||
| + | ;; Attempt to move every live monster | ||
| + | ;; towards the player | ||
| + | ;; | ||
| + | move_mon: | ||
| + | LDELM @OBJS_BASE | ||
| + | move_mon_loop: | ||
| + | LDELM [ELM] | ||
| + | CMP ELM, @OBJS_BASE | ||
| + | JZ @move_mon_done | ||
| + | |||
| + | ; live monsters only | ||
| + | LDI @OBJ_ID | ||
| + | LDA [ELM+I] | ||
| + | JZ @move_mon_loop | ||
| + | LDI @OBJ_TYPE | ||
| + | LDA [ELM+I] | ||
| + | CMP A, #2 ; OBJ_TYPE_MONSTER | ||
| + | JNZ @move_mon_loop | ||
| + | |||
| + | LDI @OBJ_X | ||
| + | LDXL [ELM+I] | ||
| + | LDI @OBJ_Y | ||
| + | LDYL [ELM+I] | ||
| + | |||
| + | ; sign(PX - mx) to CL | ||
| + | LDAL [@PX] | ||
| + | CMP AL, XL | ||
| + | JZ @mm_dx_zero | ||
| + | JC @mm_dx_pos | ||
| + | LDCL #$FF ; PX < mx, step left | ||
| + | JMP @mm_dx_done | ||
| + | |||
| + | mm_dx_pos: | ||
| + | LDCL #$01 | ||
| + | JMP @mm_dx_done | ||
| + | |||
| + | mm_dx_zero: | ||
| + | LDCL #$00 | ||
| + | |||
| + | mm_dx_done: | ||
| + | LDAL [@PY] | ||
| + | CMP AL, YL | ||
| + | JZ @mm_dy_zero | ||
| + | JC @mm_dy_pos | ||
| + | LDDL #$FF | ||
| + | JMP @mm_dy_done | ||
| + | |||
| + | mm_dy_pos: | ||
| + | LDDL #$01 | ||
| + | JMP @mm_dy_done | ||
| + | |||
| + | mm_dy_zero: | ||
| + | LDDL #$00 | ||
| + | |||
| + | mm_dy_done: | ||
| + | ; Pick axis | ||
| + | CMP CL, #0 | ||
| + | JNZ @mm_dx_nz | ||
| + | CMP DL, #0 | ||
| + | JZ @move_mon_loop | ||
| + | JMP @mm_use_y | ||
| + | |||
| + | mm_dx_nz: | ||
| + | CMP DL, #0 | ||
| + | JZ @mm_use_x | ||
| + | |||
| + | ; Both non-zero; coin flip | ||
| + | LDAH $00 | ||
| + | INT 0x13 | ||
| + | MOD B, #2 | ||
| + | CMP B, #0 | ||
| + | JZ @mm_use_x | ||
| + | ; fall through to mm_use_y | ||
| + | |||
| + | mm_use_y: | ||
| + | ADD YL, DL ; YL = my + sign(dy) | ||
| + | JMP @mm_check | ||
| + | |||
| + | mm_use_x: | ||
| + | ADD XL, CL ; XL = mx + sign(dx) | ||
| + | |||
| + | mm_check: | ||
| + | LDAL [@PX] | ||
| + | CMP AL, XL | ||
| + | JNZ @mm_check_obj | ||
| + | LDAL [@PY] | ||
| + | CMP AL, YL | ||
| + | JNZ @mm_check_obj | ||
| + | |||
| + | ; Target tile IS the player, so attack instead of move | ||
| + | LDFLD @player_obj | ||
| + | |||
| + | CALL @do_combat | ||
| + | JMP @move_mon_loop | ||
| + | |||
| + | mm_check_obj: | ||
| + | PUSH ELM | ||
| + | CALL @has_mon | ||
| + | JC @mm_pop_skip | ||
| + | CALL @is_walkable | ||
| + | JNC @mm_pop_skip | ||
| + | |||
| + | ; commit the move | ||
| + | POP ELM | ||
| + | SED | ||
| + | LDI @OBJ_X | ||
| + | STXL [ELM+I] | ||
| + | LDI @OBJ_Y | ||
| + | STYL [ELM+I] | ||
| + | CLD | ||
| + | JMP @move_mon_loop | ||
| + | |||
| + | mm_pop_skip: | ||
| + | POP ELM | ||
| + | JMP @move_mon_loop | ||
| + | |||
| + | move_mon_done: | ||
| + | RET | ||
| + | </ | ||
| + | |||
| + | And now, the combat function. This is just an example; every attack does 1 damage to the defender. You could expand on this later. | ||
| + | |||
| + | === combat.sda | ||
| + | <codify armasm> | ||
| + | player_obj: | ||
| + | ; 40 bytes, about sizeof(OBJ) | ||
| + | .bytes 0, | ||
| + | .bytes 0, | ||
| + | .bytes 0, | ||
| + | .bytes 0, | ||
| + | |||
| + | str_you: | ||
| + | .bytes "You ", 0 | ||
| + | str_the: | ||
| + | .bytes "The ", 0 | ||
| + | str_attack: | ||
| + | .bytes " | ||
| + | str_attacks: | ||
| + | .bytes " | ||
| + | str_rat: | ||
| + | .bytes "rat ", 0 | ||
| + | str_snake: | ||
| + | .bytes "snake ", 0 | ||
| + | str_spider: | ||
| + | .bytes " | ||
| + | str_unknown: | ||
| + | .bytes "thing ", 0 | ||
| + | str_dies: | ||
| + | .bytes " | ||
| + | str_you_die: | ||
| + | .bytes "You die!", 13, 10, 0 | ||
| + | |||
| + | copy_player_to_obj: | ||
| + | PUSH ELM | ||
| + | PUSH A | ||
| + | PUSH I | ||
| + | |||
| + | LDELM @player_obj | ||
| + | |||
| + | LDI @OBJ_DATA1 | ||
| + | LDA [@player_hp] | ||
| + | STA [ELM+I] | ||
| + | |||
| + | LDI @OBJ_DATA2 | ||
| + | LDA [@player_str] | ||
| + | STA [ELM+I] | ||
| + | |||
| + | POP I | ||
| + | POP A | ||
| + | POP ELM | ||
| + | RET | ||
| + | |||
| + | copy_obj_to_player: | ||
| + | PUSH ELM | ||
| + | PUSH A | ||
| + | PUSH I | ||
| + | LDELM @player_obj | ||
| + | |||
| + | LDI @OBJ_DATA1 | ||
| + | LDA [ELM+I] | ||
| + | STA [@player_hp] | ||
| + | |||
| + | LDI @OBJ_DATA2 | ||
| + | LDA [ELM+I] | ||
| + | STA [@player_str] | ||
| + | POP I | ||
| + | POP A | ||
| + | POP ELM | ||
| + | RET | ||
| + | |||
| + | |||
| + | print_mon_name: | ||
| + | PUSH ELM | ||
| + | PUSH A | ||
| + | PUSH I | ||
| + | LDI @OBJ_VIS | ||
| + | LDAL [ELM+I] | ||
| + | CMP AL, #' | ||
| + | JZ @pmn_rat | ||
| + | CMP AL, #' | ||
| + | JZ @pmn_snake | ||
| + | CMP AL, #' | ||
| + | JZ @pmn_spider | ||
| + | LDELM @str_unknown | ||
| + | JMP @pmn_emit | ||
| + | pmn_rat: | ||
| + | LDELM @str_rat | ||
| + | JMP @pmn_emit | ||
| + | pmn_snake: | ||
| + | LDELM @str_snake | ||
| + | JMP @pmn_emit | ||
| + | pmn_spider: | ||
| + | LDELM @str_spider | ||
| + | pmn_emit: | ||
| + | CALL @print_msg | ||
| + | POP I | ||
| + | POP A | ||
| + | POP ELM | ||
| + | RET | ||
| + | |||
| + | |||
| + | |||
| + | ;;;;;;;;;;;;;;;;;;;;;;;;;; | ||
| + | ;; Call with: | ||
| + | ;; ELM is the attacker | ||
| + | ;; FLD is the defender | ||
| + | ;; Note: If player is attacking or defending, | ||
| + | ;; pass @player_obj in ELM or FLD as appropriate. | ||
| + | ;; If the player is not involved, syncing doesn' | ||
| + | ;; | ||
| + | do_combat: | ||
| + | ; sync | ||
| + | CALL @copy_player_to_obj | ||
| + | |||
| + | combat_damage: | ||
| + | ; part 2: damage | ||
| + | LDI @OBJ_DATA1 | ||
| + | LDA [FLD+I] | ||
| + | JZ @combat_msg | ||
| + | DEC AL | ||
| + | STAL [FLD+I] | ||
| + | |||
| + | combat_msg: | ||
| + | CMP ELM, @player_obj | ||
| + | JZ @combat_msg_player | ||
| + | PUSH ELM | ||
| + | LDELM @str_the | ||
| + | CALL @print_msg | ||
| + | POP ELM | ||
| + | |||
| + | CALL @print_mon_name | ||
| + | PUSH ELM | ||
| + | LDELM @str_attacks | ||
| + | CALL @print_msg | ||
| + | POP ELM | ||
| + | JMP @combat_check_dead | ||
| + | |||
| + | combat_msg_player: | ||
| + | PUSH ELM | ||
| + | LDELM @str_you | ||
| + | CALL @print_msg | ||
| + | LDELM @str_attack | ||
| + | CALL @print_msg | ||
| + | POP ELM | ||
| + | |||
| + | combat_check_dead: | ||
| + | LDI @OBJ_DATA1 | ||
| + | LDA [FLD+I] | ||
| + | JNZ @combat_post | ||
| + | CALL @kill_obj | ||
| + | |||
| + | combat_post: | ||
| + | CALL @copy_obj_to_player | ||
| + | |||
| + | combat_done: | ||
| + | RET | ||
| + | |||
| + | |||
| + | kill_obj: | ||
| + | ; Monster dies | ||
| + | ; player will be handled in main loop | ||
| + | PUSH ELM ; save caller' | ||
| + | MOV ELM, FLD ; ELM = victim for everything below | ||
| + | |||
| + | PUSH ELM | ||
| + | LDELM @str_the | ||
| + | CALL @print_msg | ||
| + | POP ELM | ||
| + | CALL @print_mon_name | ||
| + | PUSH ELM | ||
| + | LDELM @str_dies | ||
| + | CALL @print_msg | ||
| + | POP ELM | ||
| + | |||
| + | LDI @OBJ_ID | ||
| + | LDA #0 | ||
| + | STA [ELM+I] | ||
| + | |||
| + | POP ELM ; restore attacker | ||
| + | RET | ||
| + | |||
| + | |||
| + | kill_player: | ||
| + | PUSH ELM | ||
| + | LDELM @str_you_die | ||
| + | CALL @print_msg | ||
| + | POP ELM | ||
| + | |||
| + | ; CLS | ||
| + | LDAH $10 | ||
| + | INT $10 | ||
| + | |||
| + | ; Print "You died." | ||
| + | LDBLX @msg_died | ||
| + | LDAH $66 | ||
| + | INT $05 | ||
| + | |||
| + | POP ELM ; get rid of return address from CALL @draw_map game loop | ||
| + | RET ; exit program. | ||
| + | </ | ||
| + | |||
| + | Finally, if the player is dead, we end the game. in '' | ||
| + | |||
| + | ... | ||
| + | CALL @draw_stats | ||
| + | CALL @draw_msgs | ||
| + | CALL @msg_display | ||
| + | | ||
| + | LDAH $50 ; VSTART | ||
| + | INT 0x18 | ||
| + | | ||
| + | LDA [@player_hp] | ||
| + | CMP A, 0 | ||
| + | JZ @kill_player | ||
| + | |||
| + | RET | ||
| + | | ||
| == Appendix III: DungeonMaker | == Appendix III: DungeonMaker | ||
sd/writing_games_in_assembly_language.1778409193.txt.gz · Last modified: by appledog
