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): RSSET USERRAM PLAYX: RS.L 0 PLAY1X: RS.W 1 ;X POSITION OF PLAYFIELD 1 PLAY2X: RS.W 1 ;X POSITION OF PLAYFIELD 2 PLAYY: RS.L 0 PLAY1Y: RS.W 1 ;Y POSITION OF PLAYFIELD 1 PLAY2Y: RS.W 1 ;Y POSITION OF PLAYFIELD 2 TEMPSCREEN: RS.B 4096 ;RAM TO BUILD TEMPORARY SCREEN MAP ENDVARS: RS.B 0 And this. Code (Text): WDEST: MACRO MOVE.L #\1+((\2)&$3FFF)<<16+(\2)>>14,VDP_CONTROL ENDM 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?
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.
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).
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.
This link may be useful, since it discusses the RS pseudoinstruction: http://antime.kapsi.fi/sega/files/SATMAN.pdf
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): rsreset obvar_id: rs.b 1 ; = 0 obvar_render: rs.b 1 ; = 1 obvar_tile: rs.w 1 ; = 2 obvar_maps: rs.l 1 ; = 4 obvar_xpos: rs.w 0 ; = 8 obvar_xscr: rs.w 1 ; = 8 obvar_yscr: rs.w 0 ; = A obvar_xsub: rs.w 1 ; = A obvar_ypos: rs.w 1 ; = C obvar_ysub: rs.w 1 ; = E obvar_xvel: rs.w 1 ; = 10 obvar_yvel: rs.w 1 ; = 12 obvar_gndvel: rs.w 1 ; = 14 obvar_yhitbox: rs.b 1 ; = 16 obvar_xhitbox: rs.b 1 ; = 17 obvar_pri: rs.b 1 ; = 18 obvar_width: rs.b 1 ; = 19 obvar_frame: rs.b 1 ; = 1A obvar_anifrm: rs.b 1 ; = 1B obvar_anim: rs.b 1 ; = 1C obvar_anirstrt: rs.b 1 ; = 1D obvar_anitime: rs.b 1 ; = 1E rs.b 1 ; = 1F obvar_coltype: rs.b 1 ; = 20 obvar_col2nd: rs.b 1 ; = 21 obvar_status: rs.b 1 ; = 22 obvar_rspwnidx: rs.b 1 ; = 23 obvar_routine: rs.b 1 ; = 24 obvar_rtn2: rs.b 1 ; = 25 obvar_angle: rs.w 1 ; = 26 obvar_subtype: rs.b 1 ; = 28
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): rsreset var1: rs.b 2 ; base offset = 0 var2: rs.b 1 ; base offset = (0 + 2*1b) = 2 var3: rs.w 1 ; base offset = (2 + 1*1b) = 3 var4: rs.b 1 ; base offset = (3 + 1*2b) = 5 var5: rs.b 0 ; base offset = (5 + 1*1) = 6 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.
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?
_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.
Sorry for the bump, but... Are you able to do a byte instruction and a word instruction at the same time? What is this?
move.b d0,($FFFFFFEC).w. How do lines like this work? I'm only used to using only one of these .datasize things.
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.