Jump to content
adamantyr

CRPG Attack Routine: First and Revised

Recommended Posts

I'm currently working on the combat engine portion of my CRPG. And I have come to the ultimate decision in any CRPG, or tabletop RPG... result determination. Or to put it frankly: "Did I hit him or not?"

 

My first attempt to code up the main routine was a straight linear approach. There are three attack types (melee, ranged, and sorcery) so I made three separate functions for each. Here's that code, excluding some minor routines on the side:

 

* Damage determination
ATKDAM BLWP @RANDOM
      ANDI R4,>0003
      A    R4,@ATKDEF+6
      BLWP @RANDOM
      ANDI R4,>0003
      A    R4,@ATKDEF+14
      MOV  @ATKDEF+14,R0
      S    @ATKDEF+6,R0
      JGT  ATKMD1
      MOV  @W1,R0
ATKMD1 MOV  @ATKDEF+2,R1
      A    @ATKDEF+4,@PSTATE+4(R1)
      MOV  @ATKDEF+10,R2
      A    R0,@PSTATE(R2)
      CLR  @RESULT
      B    @SUBRET

* Attack routine
* @ATKTYP - attack type
* @ATKDEF,@ATKDEF+8 - attacker/defender unit (0-22 x2)
* @RESULT - Combat result
ATTACK MOV  R11,*R10+
      CLR  @RESULT
* Check if defender is invulnerable
      MOV  @ATKDEF+10,R2
      MOVB @PSTATE+24(R2),R0
      JEQ  ATTCK1
      SETO @RESULT
      B    @SUBRET
* Get VDP arrays into memory
ATTCK1 MOV  @ATKDEF+2,R0
      LI   R1,HIBUFF
      LI   R2,30
      BLWP @VMBR
      MOV  @ATKDEF+10,R0
      LI   R1,HIBUFF+30
      LI   R2,30
      BLWP @VMBR
* Determine fatigue bases
      MOV  @W1,@ATKDEF+4
      MOV  @W1,@ATKDEF+12
      MOV  @ATKDEF+2,R1
      MOV  @PSTATE+12(R1),R0
      JEQ  ATTCK3
      ANDI R0,>00FF
      JEQ  ATTCK2
      DEC  @ATKDEF+4
      JMP  ATTCK3
ATTCK2 INC  @ATKDEF+4     
ATTCK3 MOV  @PSTATE+12(R2),R0
      JEQ  ATTCK5
      ANDI R0,>00FF
      JEQ  ATTCK4
      DEC  @ATKDEF+12
      JMP  ATTCK5
ATTCK4 INC  @ATKDEF+12
* Set attack and defense powers
ATTCK5 CLR  @ATKDEF+6
      CLR  @ATKDEF+14
* Check if attacker is exalted or cursed
      MOV  @PSTATE+8(R1),R0
      JEQ  ATTCK7
      ANDI R0,>00FF
      JEQ  ATTCK6
      DECT @ATKDEF+6
      JMP  ATTCK7
ATTCK6 INCT @ATKDEF+6
ATTCK7 MOV  @PSTATE+8(R2),R0
      JEQ  ATTCK9
      ANDI R0,>00FF
      DECT @ATKDEF+14
      JMP  ATTCK9
ATTCK8 INCT @ATKDEF+14
* Check if defender is guarding, if so, +2 to defense power
ATTCK9 MOV  @ATKDEF+8,R2
      MOV  @CSTATE(R2),R0
      JEQ  ATCK10
      INCT @ATKDEF+14
* Set attacker's faded state to zero
ATCK10 MOVB @ZERO,@PSTATE+21(R1)
* Add luck to power
      MOV  @ATKDEF,R1
      MOV  @ATKDEF+8,R2
      A    @PLUCK(R1),@ATKDEF+6
      A    @PLUCK(R2),@ATKDEF+14
* Branch to attack type
      MOV  @ATKTYP,R1
      B    @AKCASE(R1)

* Attack - melee
* Get base attack/defense melee power
ATKMEL CLR  @PARRY
      CLR  @BLOCK
      MOV  @ATKDEF+8,R1
      SLA  R1,1
      BL   @FDODGE
      MOVB @HIBUFF,R1
      SRL  R1,8
      A    R1,@ATKDEF+6
      MOVB @HIBUFF+30,R1
      SRL  R1,8
      A    R1,@ATKDEF+14
* Check player only defense elements
      C    @ATKDEF,@W8
      JEQ  ATKM1
* Check for 2H weapon
      CB   @HIBUFF+20,@B132
      JL   ATKM0
      CB   @HIBUFF+20,@B136
      JH   ATKM0
      INC  @ATKDEF+4
* Check if defender (player only) can parry
ATKM0  C    @ATKDEF+8,@W8
      JEQ  ATKM1
      CB   @HIBUFF+50,@ZERO
      JEQ  ATKM0A
      CB   @HIBUFF+50,@B128
      JEQ  ATKM0A
      INC  @PARRY
* Check if player can block
ATKM0A CB   @HIBUFF+52,@B147
      JNE  ATKM1
      INC  @BLOCK
* Check if attacker is enraged (+4 to attacker power)
ATKM1  MOV  @ATKDEF+2,R1
      MOVB @PSTATE+20(R1),R0
      JEQ  ATKM2
      A    @W4,@ATKDEF+6
* Check if defender is enraged (-4 to defender power)
ATKM2  MOV  @ATKDEF+10,R2
      MOVB @PSTATE+20(R2),R0
      JEQ  ATKM3
      S    @W4,@ATKDEF+14
* Check if defender is armored (+4 to defender power)
ATKM3  MOVB @PSTATE+16(R2),R0
      JEQ  ATKM4
      A    @W4,@ATKDEF+14
* Check for base miss
* Check if defender is blurred (50% miss chance)
ATKM4  MOVB @PSTATE+17(R2),R0
      JEQ  ATKM5
      LI   R3,2
      JMP  ATKM6
* Fetch base melee miss chance
ATKM5  MOVB @HIBUFF+12,R3
      SRL  R3,8
ATKM6  BLWP @RNDNUM
      MOV  R4,R4
      JNE  ATKM7
* Missed!
      MOV  @W1,@RESULT
      B    @SUBRET
* Check for critical
ATKM7  BLWP @RANDOM
      ANDI R4,>000F
      JNE  ATKM8
* Critical!
      MOV  @ATKDEF+6,R0
      SRL  R0,1
      A    R0,@ATKDEF+6
      CLR  @ATKDEF+4
      B    @ATKDAM
* Defense checks
* Check for parry
ATKM8  C    @ATKDEF+8,@W8
      JEQ  ATKM9
      MOV  @PARRY,@PARRY
      JEQ  ATKM10
ATKM9  MOVB @HIBUFF+45,R3
      SRL  R3,8
      JEQ  ATKM10
      BLWP @RNDNUM
      MOV  R3,R3
      JNE  ATKM10
* Parried!
      MOV  @W2,@RESULT
      MOV  @ATKDEF+10,R1
      A    @ATKDEF+12,@PSTATE+4(R1)
      B    @SUBRET
* Check for block
ATKM10 C    @ATKDEF+8,@W8
      JEQ  ATKM11
      MOV  @BLOCK,@BLOCK
      JEQ  ATKM12
ATKM11 MOVB @HIBUFF+46,R3
      SRL  R3,8
      JEQ  ATKM12
      BLWP @RNDNUM
      MOV  R3,R3
      JNE  ATKM12
* Blocked!
      MOV  @W3,@RESULT
      MOV  @ATKDEF+10,R1
      A    @ATKDEF+12,@PSTATE+4(R1)
      B    @SUBRET
* Check for dodge
ATKM12 MOV  @DODGE,@DODGE
      JEQ  ATKMDM
      MOV  @ATKDEF+10,R1
      MOVB @PSTATE+23(R1),R0
      JNE  ATKMDM
      MOVB @HIBUFF+47,R3
      SRL  R3,8
      JEQ  ATKMDM
      BLWP @RNDNUM
      MOV  R3,R3
      JNE  ATKMDM
* Dodged!
      MOV  @W4,@RESULT
      MOV  @DODGE,R3
      BLWP @RNDNUM
      SLA  R4,1
      MOV  @ATKDEF+10,R1
      A    @ATKDEF+12,@PSTATE+4(R1)
      INC  @PSTATE+4(R1)
      SRL  R1,3
      MOV  @DMOVE(R4),@UNITS(R1)
      B    @SUBRET
ATKMDM B    @ATKDAM

* Attack - ranged
* Get base attack/defense ranged power
ATKRNG CLR  @BLOCK
      MOVB @HIBUFF+3,R1
      SRL  R1,8
      A    R1,@ATKDEF+6
      MOVB @HIBUFF+33,R1
      SRL  R1,8
      A    R1,@ATKDEF+14
* Check player only defense elements
      C    @ATKDEF,@W8
      JEQ  ATKR1
* Check for Arbalest (-1 fatigue)
      CB   @HIBUFF+24,@B139
      JNE  ATKR0
      DEC  @ATKDEF+4
* Check if defender (player only) can block
* If attacker has firearm, nevermind
ATKR0  CB   @HIBUFF+24,@B140
      JEQ  ATKR1
      CB   @HIBUFF+52,@B147
      JNE  ATKR1
      INC  @BLOCK
* Check if defender is shielded (+4 to defender power)
ATKR1  MOV  @ATKDEF+10,R2
      MOVB @PSTATE+28(R2),R0
      JEQ  ATKR2
      A    @W4,@ATKDEF+14
* Check for base miss
* Check if defender is deflecting (50% miss chance)
ATKR2  MOVB @PSTATE+27(R2),R0
      JEQ  ATKR3
      LI   R3,2
      JMP  ATKR4
* Fetch base ranged miss chance
ATKR3  MOVB @HIBUFF+13,R3
      SRL  R3,8
ATKR4  BLWP @RNDNUM
      MOV  R4,R4
      JNE  ATKR5
* Missed!
      MOV  @W1,@RESULT
      B    @SUBRET
* Check for critical
ATKR5  BLWP @RANDOM
      ANDI R4,>000F
      JNE  ATKR6
* Critical!
      MOV  @ATKDEF+6,R0
      SRL  R0,1
      A    R0,@ATKDEF+6
      CLR  @ATKDEF+4
      B    @ATKDAM
* Defense checks
* Check for block
ATKR6  C    @ATKDEF+8,@W8
      JEQ  ATKR7
      MOV  @BLOCK,@BLOCK
      JEQ  ATKRDM
ATKR7  MOVB @HIBUFF+36,R3
      SRL  R3,8
      JEQ  ATKRDM
      BLWP @RNDNUM
      MOV  R3,R3
      JNE  ATKRDM
* Blocked!
      MOV  @W3,@RESULT
      MOV  @ATKDEF+10,R1
      A    @ATKDEF+12,@PSTATE+4(R1)
      B    @SUBRET
ATKRDM B    @ATKDAM

* Attack - sorcery (target and area)
ATKSOR CLR  @BLOCK
      MOVB @HIBUFF+6,R1
      SRL  R1,8
      A    R1,@ATKDEF+6
      MOVB @HIBUFF+36,R1
      SRL  R1,8
      A    R1,@ATKDEF+14
* Get spell type
      LI   R0,SPLDAT
      BLWP @VSBR
      SRL  R1,4
      MOVB R1,@WORK
* Check player only defense elements
      C    @ATKDEF,@W8
      JEQ  ATKS2
* Check for spell affinity
      CB   @HIBUFF+19,@WORK
      JNE  ATKS0
      INCT @ATKDEF+6
      DEC  @ATKDEF+4
ATKS0  C    @ATKDEF+8,@W8
      JEQ  ATKS1
      CB   @HIBUFF+49,@WORK
      JNE  ATKS1
      INCT @ATKDEF+14
      DEC  @ATKDEF+12
* Check if defender can block
ATKS1  CB   @HIBUFF+52,@B147
      JL   ATKS2
      CB   @HIBUFF+52,@B148
      JH   ATKS2
      INC  @BLOCK
* Check if defender is resolved (+4 to defender power)
ATKS2  MOV  @ATKDEF+10,R2
      MOVB @PSTATE+26(R2),R0
      JEQ  ATKS3
      A    @W4,@ATKDEF+14
* Check if defender is warded (50% miss chance)
ATKS3  MOVB @PSTATE+29(R2),R0
      JEQ  ATKS4
      LI   R3,2
      JMP  ATKS5
* Check for base sorcery miss (targeted only)
ATKS4  C    @W6,@ATKTYP
      JEQ  ATKS7
      MOVB @HIBUFF+14,R3
      SRL  R3,8
ATKS5  BLWP @RNDNUM
      MOV  R4,R4
      JNE  ATKS6
* Missed!
      MOV  @W1,@RESULT
      B    @SUBRET
* Check for critical (targeted only)
ATKS6  BLWP @RANDOM
      ANDI R4,>000F
      JNE  ATKS7
* Critical!
      MOV  @ATKDEF+6,R0
      SRL  R0,1
      A    R0,@ATKDEF+6
      CLR  @ATKDEF+4
      JMP  ATKSDM
* Defense checks
* Check for block (area only)
ATKS7  C    @W6,@ATKTYP
      JNE  ATKS9
      C    @ATKDEF+8,@W8
      JEQ  ATKS8
      MOV  @BLOCK,@BLOCK
      JEQ  ATKSDM
ATKS8  MOVB @HIBUFF+46,R3
      SRL  R3,8
      JEQ  ATKSDM
      BLWP @RNDNUM
      MOV  R3,R3
      JNE  ATKSDM
* Blocked!
      MOV  @W3,@RESULT
      MOV  @ATKDEF+10,R1
      A    @ATKDEF+12,@PSTATE+4(R1)
      B    @SUBRET
* Check for resist (targeted only, non-damage only)
ATKS9  MOVB @HIBUFF+48,R3
      SRL  R3,8
      JEQ  ATKSDM
      BLWP @RNDNUM
      MOV  R3,R3
      JNE  ATKSDM
* Resisted!
      MOV  @W5,@RESULT
      MOV  @ATKDEF+10,R1
      A    @ATKDEF+12,@PSTATE+4(R1)
      B    @SUBRET
* Damage determination
ATKSDM 
      B    @ATKDAM       

 

For my CRPG design, I'm less concerned about speed than I am about space. So I'm always checking the byte count any time I finish a large block of new code. This block weighs in at 1168 bytes. Ouch. :( I only have about 9k of codespace left, and I still have a lot left to do, including the quest engine, NPC interaction, combat special effects... Need to trim it down!

 

Adamantyr

Share this post


Link to post
Share on other sites

To continue...

 

One of the first things I did was expand my player data array so that some of my factors, such as fatigue costs, are just stored values instead of calculated on the spot. The cost in data space is cheap in comparison to the code space.

 

The second thing was to categorize my attack/defense adjustments into a data array that is then ran through and applied for each attack type. Only a single function is used, with a switch variable to indicate the attack type. This recycles and reuses a lot of code and cuts down on duplication.

 

So here's the new block, including the data array:

 

* Attack routine
* @ATKTYP - Attack type (0,2,4)
*           (Melee,Ranged,Sorcery)
* @ATKDEF,@ATKDEF+2 - attacker/defender PSTATE location
* @COMDEF,@COMDEF+32 - attacker/defender combat array location
* @RESULT - Combat result
ATTACK MOV  R11,*R10+
      CLR  @RESULT
* Populate COMDEF array
      LI   R9,2
      LI   R8,ATKDEF
      CLR  R7
ATKC1  MOV  *R8+,R0
      C    R0,@W96
      JH   ATKC2
      MPY  @W3,R0
      LI   R0,PLAYER+32
      A    R1,R0
      JMP  ATKC3
ATKC2  LI   R0,MONREC+6
ATKC3  LI   R1,COMDEF
      A    R7,R1
      LI   R2,32
      BLWP @VMBR
      AI   R7,32
      DEC  R9
      JNE  ATKC1       
* Process adjustments
      MOV  @ATKTYP,R6
      SRL  R6,1
      MPY  @W5,R6
      SRL  R7,8
      LI   R0,SPLDAT
      BLWP @VSBR
      SRL  R1,4
      MOVB R1,R8
      LI   R9,2
      LI   R2,ATKADJ+6
ATK0   MOV  *R2+,R0
* Get affected units for check/change into R4/R5
ATK1   MOVB *R2+,R1
      MOVB R1,R3
      SRL  R1,12
      SRL  R3,4
      ANDI R1,>000F
      ANDI R3,>0020
      MOV  @ATKDEF(R1),R4
      LI   R5,COMDEF
      A    R3,R5
      MOVB *R2+,R3
* Check if spell affinity
      CB   R3,@B128
      JNE  ATK1A
      CB   R8,@COMDEF+26
      JEQ  ATK1B
      JMP  ATK3
* Check targeted state
ATK1A  SRL  R3,8
      A    R3,R4
      CB   *R4,@ZERO
      JEQ  ATK3
* Make adjustment
ATK1B  MOVB *R2+,R3
      SRL  R3,8
      A    R3,R1
      MOVB *R2+,R3
      CB   R3,@B7
      JGT  ATK2
      AB   R3,*R1
      JMP  ATK4
* Set the value
ATK2   SB   @B8,R3
      MOVB R3,*R1
      JMP  ATK4
ATK3   INCT R2
ATK4   DEC  R0
      JNE  ATK1
      MOV  @ATKTYP,R1
      MOV  @ATKADJ+2(R1),R2
      DEC  R9
      JNE  ATK0
* Defense adjustments for types
      LI   R0,COMDEF+25
      MOV  @ATKTYP,R1
      SLA  R1,1
      LI   R2,DEFADJ
      A    R1,R2
      LI   R3,4
ATK5   SZCB *R2+,*R0+
      DEC  R3
      JNE  ATK5
* Miss check
      MOV  @ATKTYP,R1
      SRL  R1,1
      MOVB @COMDEF+18(R1),R3
      SRL  R3,8
      BLWP @RNDNUM
      MOV  R4,R4
      JNE  ATK6
* Missed!
      MOV  @W1,@RESULT
      B    @SUBRET
* Critical check
ATK6   MOVB @COMDEF+4(R7),R0
      SRL  R0,8
      BLWP @RANDOM
      ANDI R4,>000F
      C    R4,R0
      JGT  ATK7
* Critical! (No defenses)
      SETO R9
      JMP  ATK10
* Defense check
ATK7   CLR  R9
      LI   R0,COMDEF+53
      LI   R5,4
      LI   R6,2
ATK8   MOVB *R0+,R3
      JEQ  ATK9
      SRL  R3,8
      BLWP @RNDNUM
      MOV  R4,R4
      JNE  ATK9
* Successful defense
      MOVB @COMDEF+49,R0
      SRL  R0,8
      MOV  @ATKDEF+2,R1
      A    R0,@PSTATE+4(R1)
      MOV  R6,@RESULT
      B    @SUBRET
ATK9   INC  R6
      DEC  R5
      JNE  ATK8
* Damage/debuff determination
ATK10  CLR  @RESULT
      MOVB @COMDEF(R7),R5
      SRL  R5,8
* Check if sorcery; add power value
      C    @W4,@ATKTYP
      JLT  ATK12
      LI   R0,SPLDAT+3
      BLWP @VSBR
      SRL  R1,8
      A    R1,R5
ATK11  MOVB @COMDEF+33(R7),R2
      SRL  R2,8
* Critical double value
      MOV  R9,R9
      JEQ  ATK12
      SLA  R5,1
ATK12  S    R2,R5
      JGT  ATK13
      CLR  R5
* Increase fatigue for attacker
ATK13  MOVB @COMDEF+3(R7),R0
      SRL  R0,8
      MOV  @ATKDEF,R1
      A    R5,@PSTATE+4(R1)
      C    @W4,@ATKTYP
      JLT  ATK14
* Check if sorcery debuff
      LI   R0,SPLDAT+4
      BLWP @VSWR
      CI   R1,2
      JEQ  ATK14
      MOV  R5,R0
      MOV  @ATKDEF+2,R2
      ANDI R0,>0007
      BL   @EFFCTS
      B    @SUBRET
* Straight damage to defender
ATK14  MOV  @ATKDEF+2,R1
      A    R5,@PSTATE+2(R1)
      B    @SUBRET

* Defense adjustments
DEFADJ DATA >0000,>00FF
      BYTE >FF00,>FFFF
      BYTE >FFFF,>FF00

* Attack adjustments
ATKADJ DATA ATKADJ+16,ATKADJ+66,ATKADJ+104
* General adjustments (ATKADJ+6)
      DATA 2
      BYTE 0,12,17,-1                 * Attacker - Move Empowered check
      BYTE >22,13,17,1                * Defender - Move Weakened check

* Melee adjustments (ATKADJ+16)
      DATA 12
      BYTE 0,8,0,2                    * Attacker - Melee Exalt check
      BYTE >22,8,1,2                  * Defender - Melee Exalt check
      BYTE 0,9,0,-2                   * Attacker - Melee Curse check
      BYTE >22,9,1,-2                 * Defender - Melee Curse check
      BYTE 0,12,3,-1                  * Attacker - Melee Empowered check
      BYTE 0,13,3,1                   * Attacker - Melee Weakened check
      BYTE 0,20,3,2                   * Attacker - Enraged checks
      BYTE 0,20,4,4
      BYTE 1,17,18,10                 * Defender - Blurred check
      BYTE >22,16,1,4                 * Defender - Armored check
      BYTE >22,23,23,8                * Defender - Immobilized check
      BYTE >22,24,1,127               * Defender - Melee Invulnerable check

* Ranged adjustments (ATKADJ+66)
      DATA 9
      BYTE 0,8,5,2                    * Attacker - Ranged Exalt check
      BYTE >22,8,6,2                  * Defender - Ranged Exalt check
      BYTE 0,9,5,-2                   * Attacker - Ranged Curse check
      BYTE >22,9,6,-2                 * Defender - Ranged Curse check
      BYTE 0,12,8,-1                  * Attacker - Ranged Empowered check
      BYTE 0,13,8,1                   * Attacker - Ranged Weakened check
      BYTE 2,27,19,10                 * Defender - Deflecting check
      BYTE >22,28,6,4                 * Defender - Shielded check
      BYTE >22,24,6,127               * Defender - Ranged Invulnerable check

* Sorcery Attack adjustments (ATKADJ+104)
      DATA 14
      BYTE 0,8,10,2                   * Attacker - Sorcery Exalt check
      BYTE >22,8,11,2                 * Defender - Sorcery Exalt check
      BYTE 0,9,10,-2                  * Attacker - Sorcery Curse check
      BYTE >22,9,11,-2                * Defender - Sorcery Curse check
      BYTE 0,12,13,-1                 * Attacker - Sorcery Empowered check
      BYTE 0,13,13,1                  * Attacker - Sorcery Weakened check
      BYTE 2,26,20,10                 * Defender - Resolve check
      BYTE >22,29,11,4                * Defender - Warded check
      BYTE >22,24,11,127              * Defender - Sorcery Invulnerable check
      BYTE 0,13,13,1                  * Attacker - Sorcery Weakened check
      BYTE 0,128,10,2                 * Attacker - Sorcery Affinity
      BYTE 0,128,13,-1
      BYTE >22,128,11,2               * Defender - Sorcery Affinity
      BYTE >22,128,17,-1

 

This new section and the data block take up 440 bytes and 162 bytes respectively, for a total of 602 bytes. Almost half the size, and a lot more elegant. :)

 

Adamantyr

Edited by adamantyr

Share this post


Link to post
Share on other sites

You know you can put comments on the end of some of those lines to make the code easier to read. :roll:

Share this post


Link to post
Share on other sites

You know you can put comments on the end of some of those lines to make the code easier to read. :roll:

 

Yes, indeed I can. The TI assemblers recognize anything past the opcode sections as comments.

 

But my favored editor (Textpad) isn't quite clever enough to treat them as comments. So I usually just use whole lines instead of on-line comments.

 

UPDATE: Hm, actually, now I can get it to work. I still have to use some character (an asterisk works well) but I was able to make column 40 be a comment line. Cool! Edited the above to be slightly more readable

 

Adamantyr

Edited by adamantyr

Share this post


Link to post
Share on other sites

I've been fallowing your blogs on this and I am just amazed that you are pulling this off with such tight memory constraints. The first rpg I wrote but didn't finish was a ultima 3 clone written in pascal on a 128k pc and I was feeling pressed for memory on that. Although I was a less capable coder then and stuff was much more eh, sloppy. But, still this is really an impressive feat.

 

John

Share this post


Link to post
Share on other sites

Yes, indeed I can. The TI assemblers recognize anything past the opcode sections as comments.

 

But my favored editor (Textpad) isn't quite clever enough to treat them as comments. So I usually just use whole lines instead of on-line comments.

 

UPDATE: Hm, actually, now I can get it to work. I still have to use some character (an asterisk works well) but I was able to make column 40 be a comment line. Cool! Edited the above to be slightly more readable

 

Adamantyr

Actually, I wasn't referring to the data section but that looks much better! :D

Share this post


Link to post
Share on other sites

Yes, indeed I can. The TI assemblers recognize anything past the opcode sections as comments.

 

But my favored editor (Textpad) isn't quite clever enough to treat them as comments. So I usually just use whole lines instead of on-line comments.

 

UPDATE: Hm, actually, now I can get it to work. I still have to use some character (an asterisk works well) but I was able to make column 40 be a comment line. Cool! Edited the above to be slightly more readable

 

Adamantyr

Actually, I wasn't referring to the data section but that looks much better! :D

 

I assume you mean I should have every single line (or nearly so) commented. Bah!

 

Actually, I originally started coding on the vintage system itself, so I had a real concern that my source files (which needed to fit on one disk) may get too big if I went overboard on comments.

 

That isn't the case anymore; I directly assemble my source files from PC files using A99 (Written by Joe Delekto) and/or Classic99's E/A assembler. However, since I'm the only coder, I don't feel the need to thoroughly comment the lines for myself, since I have a pretty good knowledge of what the different labels are and how the whole system is set up.

 

If I was to prepare the code base for porting to another platform, what I'd probably do is write it all up in pseudo-code, rather than just heavily comment an assembly file.

 

Adamantyr

Share this post


Link to post
Share on other sites

He he! You and I differ vastly when it comes to comments! Here's some source from my Forth project: :P

 

;[ HCHAR ( y x ascii count -- )
hcharh	data gmodeh,5
text 'HCHAR '
hchar	data $+2
bl @get4			; get parameters from stack and calculate address
bl @vsbwm			; write to screen
b *next				; see ya
;]

;[ VCHAR ( y x ascii count -- )
vcharh	data hcharh,5
text 'VCHAR '
vchar	data $+2
bl @get4			; get parameters from stack and calculate address
	
li r6,24			; row count
mpy @xmax,r6		        ; max visible address+1 (in r7)
dec r7				; correct max visible (we count from 0)
	
mov @xmax,r6		        ; get xmax in a register
	
vchar1	bl @vsbw			; write a character
a r6,r0				; move down one line
c r0,r7				; gone off end of screen?
jle vchar2			; skip if not
s r7,r0				; reduce address
vchar2	dec r2				; decrement count
jne vchar1			; repeat if not finished
b *next
;]

;[ GCHAR ( y x -- ascii )
gcharh	data vcharh,5
text 'GCHAR '
gchar	data $+2
bl @get2			; get y & x from stack
mpy @xmax,r6		        ; compute y
a r7,r0				; compute screen address
clr r1				; use r1 for byte operations
bl @vsbr			; read byte from vdp
swpb r1				; move byte to lsb
inct stack			; make space on stack
mov r1,*stack		        ; place on stack as 16 bit word
b *next
;]

;[ DCHAR ( W1..Wx  word_count ascii -- )
; loads words from the stack into VDP memory at the ASCII
; code specified. Equivalent to CALL CHAR in BASIC.
; Note: To make code easier to read, the order of the graphic data does NOT
; have to be reversed! The routine looks *down* the stack and works up,
; correcting the stack at the end of the routine.
; word_count tells the routine how many words of graphic data exist on the 
; stack.
dcharh	data gcharh,5
text 'DCHAR '
dchar	data $+2
bl @sget2			; get two parameters
sla r9,3			; multiply ascii by 8
sla r10,1			; convert word count to byte count
mov r10,r6			; copy it
inct r6				; adjust for stack correction later
s r10,stack			; adjust stack
inct stack			; point at first data word		
mov r9,r0			; ascii to r0
ai r0,>800			; point to entry in pattern table
dchar1	mov *stack,r1		        ; get data from stack
inct stack			; move back up the stack
bl @vsbw			; write byte to vdp
inc r0				; next vdp address
swpb r1				; rotate graphic data
bl @vsbw			; write byte to vdp
inc r0				; next vdp address
dect r10			; decrement count
jne dchar1			; repeat if not finished
s r6,stack			; remove everything from stack
b *next
;]

;[ SPRITE ( sprite y x ascii color -- )
; sprite attribute list begins at 6*80h=300h
sprith	data dcharh,6
text 'SPRITE'
sprite	data $+2		
bl @sget5			; get 5 parameters
sla r10,2			; multiply sprite number by 4 (offset into SAL)
	
li r11,sal			; address of SAL in CPU ram
li r0,>300			; address of SAL in VDP ram
a r10,r11			; add offset to cpu addr according to sprite number
mov r11,r1			; cpu source for vmbw
a r10,r0			; destination address for vmbw

swpb r6				; rotate colour
swpb r7				; rotate ascii
swpb r8				; rotate x
swpb r9				; rotate y
	
movb r9,*r11+		        ; move y to cpu buffer
movb r8,*r11+		        ; move x to cpu buffer
movb r7,*r11+		        ; moce ascii to cpu buffer
movb r6,*r11+		        ; move colour to cpu buffer
	
li r2,4
bl @vmbw
sprtx	b *next
;]

;[ MAGNIFY ( x -- )
; sets sprite magnification:
; only the least significant bits are used:
; bit 7: 1=magnified (0=not magnified)
; bit 6: 1=double size (4 character)
; Remember: TI number their bits backwards! Idiots!
magfyh	data sprith,7
	text 'MAGNIFY '
magfy	data $+2
bl @sget1			; get one parameter from stack (r10)
swpb r10			; get value in msb
andi r10,>0300		        ; mask out any crap

li r0,>0001			; vdp register number in lsb
clr r2				; prepare for byte operations

movb @VDPR1,r2		        ; get copy of VDP R1
andi r2,>fc00		        ; mask out magnification bits
socb r2,r10			; OR in new magnification value

movb r10,r0			; place in r0 msb
movb r0,@>83d4		        ; place copy in 83d4
movb r0,@VDPR1		        ; reserve copy (VDP regs are read only)
swpb r0				; rotate
bl @vwtr			; set the register
b *next
;]

;[ SPRCOL ( sprite colour -- )
; sets the colour of a sprite
sprclh	data magfyh,6
text 'SPRCOL'
sprcol	data $+2
bl @sget2			; get 2 parameters: colour=r9, sprite=r10
li r0,>300+3		        ; SAL in vdp (offset to colour byte added)
li r8,SAL+3			; SAL in CPU (offset to colour byte added)
sla r10,2			; multiply sprite number by 4
	
a r10,r0			; point to correct address in vdp
a r10,r8			; point to correct address in CPU SAL
	
swpb r9				; rotate colour into MSB
mov r9,r1			; into r1 for VSBW
movb r9,*r8			; load into CPU SAL
bl @vsbw			; write colour byte into VDP
b *next
;]

;[ SPRLOC ( sprite y x -- )
; sets the location of a sprite
sprlch	data sprclh,6
text 'SPRLOC'
sprloc	data $+2
bl @sget3			; get 3 parameters from stack
li r0,>300			; address of SAL in VDP
li r1,SAL			; address of SAL in CPU
sla r10,2			; get offset into tables
a r10,r0			; add to vdp addr
a r10,r1			; add to cpu addr
swpb r8				; rotate x
swpb r9				; rotate y
movb r9,*r1+		        ; write y to cpu SAL
movb r8,*r1			; write x to cpu SAL
dec r1				; point to beginning of entry in SAL
li r2,2				; two bytes to write
bl @vmbw			; write to VDP
b *next
;]

;[ SPRLOC? ( sprite -- y x )
; gets the location of a sprite
locsph	data sprlch,7
text 'SPRLOC? '
locspr	data $+2
bl @sget1			; get sprite number (r10)
li r0,SAL			; address of SAL in CPU ram
sla r10,2			; get offset
a r10,r0			; point to correct address in SAL
clr r1				; prepare for byte operations
movb *r0+,r1    		; get y and point to x
swpb r1				; move to lsb
inct stack			; make space on stack
mov r1,*stack+	        	; place on stack and make new stack entry
clr r1
movb *r0,r1			; get x
swpb r1				; move to lsb
mov r1,*stack    		; place on stack
b *next
;]

;[ SPRPAT ( sprite ascii -- )
; sets the pattern of a sprite
sppath	data locsph,6
text 'SPRPAT'
sprpat	data $+2
bl @sget2			; get 2 parameters
li r0,>300+2		        ; address of SAL in vdp
li r2,SAL+2			; address of SAL in cpu
sla r10,2			; calculate offset
a r10,r0			; offset into vdp
a r10,r2			; offset into cpu
swpb r9				; rotate ascii into msb
mov r9,r1			; for vsbw
movb r9,*r2			; set in cpu ram
bl @vsbw			; set in vdp ram
b *next
;]

 

Comments everywhere! This is because I do not trust myself to understand my own code in 6 months time! :ponder:

Share this post


Link to post
Share on other sites

I'm used to doing consulting work where I'll work on a project for 6 months or a year and then I'm gone. I have to write code someone else can understand.

 

I've also had to verify compliance with govt regulations so I tend to follow the same rules myself.

Share this post


Link to post
Share on other sites

Complaining about the lack of comments, especially in a retro project? ;)

 

Glad to see the CRPG is moving along nicely, the recent spat of updates on your blog is inspiration for my own CRPG project.

Share this post


Link to post
Share on other sites

I use as many comments as I need for myself right now, as I'm the only coder on my project. If I was coding for a group, I'd have more judiciously spread around.

 

However, with assembly, most of the operations are pretty obvious as a glance; the comments are really just there to detail what particular labels mean. I actually fully commented a 6502 listing of Eastern Front: 1941 before I started trying to work on a conversion... and it didn't help all that much. The problem being that you really don't grasp WHAT the code is trying to do on a whole, only what it literally does. I wasn't able to figure out the top-down design of the AI by studying the code line-by-line, it would require a lot more research to do so.

 

As for forgetting stuff after six months... yeah, that could happen to me too, but that's why I have spreadsheets and other tools to track a lot of the "soft" data that makes up the game engine.

 

Adamantyr

Share this post


Link to post
Share on other sites
Complaining about the lack of comments, especially in a retro project? ;)

 

Not every contrary post is a complaint. :) I think people are just showing their own take on a concept.

 

This project is, of course, one of the most impressive ones I've had the pleasure of seeing running. There are so many neat little features in it.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...