User Tools

Site Tools


sd:writing_games_in_assembly_language

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
sd:writing_games_in_assembly_language [2026/05/10 10:52] appledogsd: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    ; Skip non-initialized/empty objects     JZ @draw_items_loop    ; Skip non-initialized/empty objects
  
 +    LDI @OBJ_TYPE
 +    LDA [ELM+I]
 +    CMP A, #1
 +    JNZ @draw_items_loop         ; not amn item. skip
 +    
     ; 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 ---+    ; 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      ; wrapped to head, no match → keep map glyph+    JZ @get_glyph_done      ; wrapped to head, no matchkeep map glyph
  
     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's VIS, then exit loop+    ; Match will override AL with the object's VIS, then exit loop
     LDI @OBJ_VIS     LDI @OBJ_VIS
     LDAL [FLD+I]     LDAL [FLD+I]
Line 3383: Line 3388:
  
 ==== move_mon ==== move_mon
-This is where we attempt to move the monsters.+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 ''CALL @move_mon'' before ''CALL @draw_mon'' in ''draw_map:''.
  
 <codify armasm> <codify armasm>
Line 3392: Line 3418:
 ;; towards the player ;; towards the player
 ;; ;;
 +
 move_mon: move_mon:
     LDELM @OBJS_BASE     LDELM @OBJS_BASE
Line 3408: Line 3435:
     JNZ @move_mon_loop     JNZ @move_mon_loop
  
-    ; --- (mx, my) --- 
     LDI @OBJ_X     LDI @OBJ_X
     LDXL [ELM+I]     LDXL [ELM+I]
Line 3442: Line 3468:
 mm_dy_done: mm_dy_done:
  
-    ; --- Pick axis ---+    ; Pick axis
     CMP CL, #0     CMP CL, #0
     JNZ @mm_dx_nz     JNZ @mm_dx_nz
Line 3453: Line 3479:
     JZ  @mm_use_x     JZ  @mm_use_x
  
-    ; Both non-zero — coin flip+    ; Both non-zero (coin flip)
     LDAH $00     LDAH $00
     INT 0x13     INT 0x13
Line 3468: Line 3494:
  
 mm_check: mm_check:
-    ; Don't step on the player (combat goes here later)+    ; Don't step on the player
     LDAL [@PX]     LDAL [@PX]
     CMP AL, XL     CMP AL, XL
Line 3477: Line 3503:
  
 mm_check_obj: mm_check_obj:
-    PUSH ELM                    ; has_mon overwrites ELM on hit; is_walkable goes through get_glyph+    PUSH ELM
     CALL @has_mon     CALL @has_mon
     JC  @mm_pop_skip            ; another monster occupies target     JC  @mm_pop_skip            ; another monster occupies target
Line 3498: Line 3524:
     RET     RET
 </codify> </codify>
 +
 +=== 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/monster at the start and end. That will be good enough for now.
 +
 +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             ; PX > mx
 +    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         ; both zero, monster sits on player, skip
 +    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           ; ELM attacks FLD
 +
 +    CALL @do_combat
 +    JMP @move_mon_loop          ; monster's turn is spent attacking
 +
 +mm_check_obj:
 +    PUSH ELM
 +    CALL @has_mon
 +    JC  @mm_pop_skip            ; another monster occupies target
 +    CALL @is_walkable
 +    JNC @mm_pop_skip            ; wall or other non-walkable
 +
 +    ; 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
 +</codify>
 +
 +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,0,0,0,0,0,0,0,0,0
 +    .bytes 0,0,0,0,0,0,0,0,0,0
 +    .bytes 0,0,0,0,0,0,0,0,0,0
 +    .bytes 0,0,0,0,0,0,0,0,0,0
 +
 +str_you:
 +    .bytes "You ", 0
 +str_the:
 +    .bytes "The ", 0
 +str_attack:
 +    .bytes "attack for 1 damage!", 13, 10, 0   ; player verb
 +str_attacks:
 +    .bytes "attacks for 1 damage!", 13, 10, 0  ; monster verb
 +str_rat:
 +    .bytes "rat ", 0
 +str_snake:
 +    .bytes "snake ", 0
 +str_spider:
 +    .bytes "spider ", 0
 +str_unknown:
 +    .bytes "thing ", 0
 +str_dies:
 +    .bytes "dies!", 13, 10, 0
 +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, #'r'
 +    JZ  @pmn_rat
 +    CMP AL, #'s'
 +    JZ  @pmn_snake
 +    CMP AL, #'x'
 +    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't hurt.
 +;;
 +do_combat:
 +    ; sync
 +    CALL @copy_player_to_obj
 +
 +combat_damage:
 +    ; part 2: damage
 +    LDI @OBJ_DATA1
 +    LDA [FLD+I]
 +    JZ  @combat_msg                   ; defender already 0 (defensive)
 +    DEC AL
 +    STAL [FLD+I]                      ; HP -= 1, stored (may be 0)
 +
 +combat_msg:
 +    CMP ELM, @player_obj
 +    JZ  @combat_msg_player
 +    PUSH ELM
 +    LDELM @str_the
 +    CALL @print_msg
 +    POP ELM
 +
 +    CALL @print_mon_name              ; ELM = attacker
 +    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's attacker
 +    MOV ELM, FLD                    ; ELM = victim for everything below
 +
 +    PUSH ELM
 +    LDELM @str_the
 +    CALL @print_msg
 +    POP ELM
 +    CALL @print_mon_name              ; reads OBJ_VIS via ELM = victim
 +    PUSH ELM
 +    LDELM @str_dies
 +    CALL @print_msg
 +    POP ELM
 +
 +    LDI @OBJ_ID
 +    LDA #0
 +    STA [ELM+I]                       ; free the slot
 +
 +    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.
 +</codify>
 +
 +Finally, if the player is dead, we end the game. in ''draw_map:''
 +
 +    ...
 +    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.1778410333.txt.gz · Last modified: by appledog

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki