don't click here

Less-documented 68K instructions?

Discussion in 'Technical Discussion' started by chilistudios, Jun 17, 2024.

  1. I'm studying 68000 assembly, and I'm using Jon Burton's code as a reference. While looking through his code, I found stuff like this...
    Code (Text):
    1.  
    2.         RSSET    USERRAM
    3. PLAYX:        RS.L    0
    4. PLAY1X:        RS.W    1            ;X POSITION OF PLAYFIELD 1
    5. PLAY2X:        RS.W    1            ;X POSITION OF PLAYFIELD 2
    6. PLAYY:        RS.L    0
    7. PLAY1Y:        RS.W    1            ;Y POSITION OF PLAYFIELD 1
    8. PLAY2Y:        RS.W    1            ;Y POSITION OF PLAYFIELD 2
    9. TEMPSCREEN:    RS.B    4096            ;RAM TO BUILD TEMPORARY SCREEN MAP
    10. ENDVARS:    RS.B    0
    11.  
    And this.
    Code (Text):
    1.  
    2. WDEST:        MACRO
    3.         MOVE.L    #\1+((\2)&$3FFF)<<16+(\2)>>14,VDP_CONTROL
    4.         ENDM
    5.  
    What's a RSSET and a RS? And I was never told that I could do that stuff in a move instruction. Where do I find more information about these instructions?
     
    Last edited: Jun 18, 2024
  2. Kilo

    Kilo

    That inbetween sprite from S&K's title screen Tech Member
    557
    556
    93
    Canada
    S1 - Metal Sonic's Challenge, Sonic 1 Rev01 ASMX Disasm
    It's essentially the same as ds, which essentially serves as enums in modern programming. Useful for organizing memory rather than using equates and hard assigning variables.
    As for rsset, it let's you assign the starting point of these 'enums', often set to the start of the Mega Drive's RAM, $FF0000. There's also rsreset which brings it back to 0.

    As for the move instruction it's an expression that gets calculated at assemble time. Since the Mega Drive's VDP does all sorts of bit shifting to encode read/write/DMA commands along with their destination and length having these expressions makes the code more readable, which is what's shown here.
     
    Last edited: Jun 18, 2024
    • Agree Agree x 2
    • Informative Informative x 1
    • List
  3. Devon

    Devon

    A̸ ̴S̴ ̵C̵ ̷E̶ ̸N̸ ̴D̶ ̵E̶ ̸D̶ Tech Member
    1,303
    1,513
    93
    your mom
    Not just enums, but data structures, too. But yeah, it's assembler syntax, not actually part of the m68k instruction set. You can even find them in ASMSH, which assembles Hitachi SuperH code (like for the 32X or Saturn).
     
  4. Cooljerk

    Cooljerk

    Professional Electromancer Oldbie
    4,613
    292
    63
    most homebrew sites give a pretty poor example of how to use the _RS counter, but the SN Systems N64 development manual has really good documentation on them:

    https://ultra64.ca/files/documentat...PC_Development_System_for_the_Nintendo_64.pdf

    in short, as the others said, it's used for packing structures/enums/etc. _RS is a counter that you use to calculate the offset of the variables in a struct, according to the size of the Rx command. RSRESET is used to reset the _RS counter which is used in calculating the offset so each new instance of it begins at 0.
     
    Last edited: Jun 18, 2024
  5. Brainulator

    Brainulator

    Regular garden-variety member Member
  6. Is there any place where I can find ASM68K's syntax?
     
  7. Devon

    Devon

    A̸ ̴S̴ ̵C̵ ̷E̶ ̸N̸ ̴D̶ ̵E̶ ̸D̶ Tech Member
    1,303
    1,513
    93
    your mom
    It's exactly the same. Check Brainulator's post.
     
  8. Kilo

    Kilo

    That inbetween sprite from S&K's title screen Tech Member
    557
    556
    93
    Canada
    S1 - Metal Sonic's Challenge, Sonic 1 Rev01 ASMX Disasm
    Here's the object status variables from one of my projects that should hopefully give you an idea on how it's used
    Code (Text):
    1.         rsreset
    2. obvar_id:        rs.b 1    ; = 0
    3. obvar_render:    rs.b 1    ; = 1
    4. obvar_tile:        rs.w 1    ; = 2
    5. obvar_maps:        rs.l 1    ; = 4
    6. obvar_xpos:        rs.w 0    ; = 8
    7. obvar_xscr:        rs.w 1    ; = 8
    8. obvar_yscr:        rs.w 0    ; = A
    9. obvar_xsub:        rs.w 1    ; = A
    10. obvar_ypos:        rs.w 1    ; = C
    11. obvar_ysub:        rs.w 1    ; = E
    12. obvar_xvel:        rs.w 1    ; = 10
    13. obvar_yvel:        rs.w 1    ; = 12
    14. obvar_gndvel:    rs.w 1    ; = 14
    15. obvar_yhitbox:    rs.b 1    ; = 16
    16. obvar_xhitbox:    rs.b 1    ; = 17
    17. obvar_pri:        rs.b 1    ; = 18
    18. obvar_width:    rs.b 1    ; = 19
    19. obvar_frame:    rs.b 1    ; = 1A
    20. obvar_anifrm:    rs.b 1    ; = 1B
    21. obvar_anim:        rs.b 1    ; = 1C
    22. obvar_anirstrt:    rs.b 1    ; = 1D
    23. obvar_anitime:    rs.b 1    ; = 1E
    24.                 rs.b 1    ; = 1F
    25. obvar_coltype:    rs.b 1    ; = 20
    26. obvar_col2nd:    rs.b 1    ; = 21
    27. obvar_status:    rs.b 1    ; = 22
    28. obvar_rspwnidx:    rs.b 1    ; = 23
    29. obvar_routine:    rs.b 1    ; = 24
    30. obvar_rtn2:        rs.b 1    ; = 25
    31. obvar_angle:    rs.w 1    ; = 26
    32. obvar_subtype:    rs.b 1    ; = 28
     
  9. Cooljerk

    Cooljerk

    Professional Electromancer Oldbie
    4,613
    292
    63
    In case it's not obvious, the number after the Rx opcode is like an array count. It'll give you the starting offset of the base variable, but you'll have that number of size Rx bytes between the next offset. So if you were like:

    Code (Text):
    1.         rsreset
    2. var1:        rs.b 2    ; base offset = 0
    3. var2:        rs.b 1    ; base offset = (0 + 2*1b) = 2
    4. var3:        rs.w 1    ; base offset = (2 + 1*1b) = 3
    5. var4:        rs.b 1    ; base offset = (3 + 1*2b) = 5
    6. var5:        rs.b 0    ; base offset = (5 + 1*1) = 6
    7. var6:        rs.b 1    ; base offset = (6 + 0*1) = 6
    var2's starting offset is 2 bytes past var1's starting offset, when type of var1 is a byte. So there are 2 var1's in the struct in an array, essentially. Var3 is a word, which is 2 bytes, and there is 1 of it, so var4's base offset is 3 + 1*2 bytes = 5 bytes. Var5 has a count of 0, so var6's base offset is the same as var5's.
     
    Last edited: Jun 19, 2024
  10. I'm glad that you guys showed me that what I'm looking for is in the Psy-Q Saturn manual, but I now have a new question. What is the RS counter?
     
  11. Cooljerk

    Cooljerk

    Professional Electromancer Oldbie
    4,613
    292
    63
    _RS is the internal counter the preprocessor uses to figure out the byte offset. When you assemble a source code, the first step is a proprocessor to go through the source code and look at a bunch of macros and assembler-specific syntax to modify. For example, macros will be replaced in the source code with the appropriate code, copy-and-pasted into place.

    There are a few commands to control the RS counter so the assembler will work correctly. RSRESET will set the _RS counter to 0. Every time you evoke an rs.x command, what is going on behind the scenes is that the preprocessor is changing each label to the appropriate offset by using the formula offset = _RS + (Rs.x * Count), where Rs.x is the size of the Rs command in bytes (1 byte, 2 bytes = word, etc) and Count is the number of elements in the array size you are using. After each time you evoke an Rs.x command, the counter in _RS is increased by the size of the command, so the formula keeps working. This is why, when you want to pack a new struct/enum, you need to reset the counter, as the assembler needs to know when to go back to 0 to start formulating new offsets.
     
    Last edited: Jun 24, 2024
  12. Sorry for the bump, but...
    Screenshot 2024-07-11 204206.png
    Are you able to do a byte instruction and a word instruction at the same time? What is this?
     
  13. Devon

    Devon

    A̸ ̴S̴ ̵C̵ ̷E̶ ̸N̸ ̴D̶ ̵E̶ ̸D̶ Tech Member
    1,303
    1,513
    93
    your mom
    Could you point out exactly what you mean?
     
  14. move.b d0,($FFFFFFEC).w.

    How do lines like this work? I'm only used to using only one of these .datasize things.
     
  15. Devon

    Devon

    A̸ ̴S̴ ̵C̵ ̷E̶ ̸N̸ ̴D̶ ̵E̶ ̸D̶ Tech Member
    1,303
    1,513
    93
    your mom
    Okay, so that second one has to do with a feature with the 68000 where you can store addresses as 16-bit or 32-bit values. 16-bit values, when read, get sign extended into 32-bit values (i.e. 0x8000 -> 0xFFFF8000, 0x0020 -> 0x00000020). Since the 68000 has a 24-bit address bus, the topmost byte will get ignored. This is why you see RAM addresses 0xFF0000 to 0xFF7FFF stored as 32-bit values, and RAM addresses 0xFF8000 to 0xFFFFFF stored as 16-bit values (the topmost byte is kept in the code itself for ASM68K to parse it as a sign extended 32-bit value properly). Doing this makes instructions smaller and addresses quicker to read (only 1 word read that gets internally sign extended, as opposed to 2 word reads).

    So, all that line is doing is storing the low byte of d0 into memory address 0xFFFFEC, with 0xFFFFEC being referred to as "($FFFFFFEC).w" so that it gets stored as a 16-bit value in the instruction data itself to save a little on ROM space and make the instruction a little quicker.