Author Topic: SH3 programming tutorials  (Read 5762 times)

0 Members and 1 Guest are viewing this topic.

Offline AngelFish

  • Is this my custom title?
  • Administrator
  • LV12 Extreme Poster (Next: 5000)
  • ************
  • Posts: 3242
  • Rating: +270/-27
  • I'm a Fishbot
    • View Profile
SH3 programming tutorials
« on: January 29, 2011, 05:03:54 am »
Introduction:

To get started in SH3 programming, a background in z80 or other low level programming is helpful, but it's not necessary. Also, the information contained herein is accurate to the best of my knowledge at the time of this posting, but may not be in the future. Be sure to double check other documentation if something doesn't seem to work.

Also, for future reference, the [current] standard documentation can be found at these links:

SH3 Hex equates
SH3 Assembly language documentation
7705 SH3 processor documentation

In order to understand SH3 hex, you need to understand the basic facts about the processor itself. The SuperH 3 processor is what's known as a 32 bit RISC processor. This means that the processor likes to operate on 32 bit chunks of data. This means that each bit (which is a binary 1 or 0 in memory) is grouped with 7 other bits to form a group of 8 bits known as a "byte." Two bytes are grouped to form something called a "Word," which has 16 bits. Two words (4 bytes or 32 bits) are grouped together into a large group known as a "Longword" in SH3 Assembly.  RISC stands for Reduced Instruction Set Computing and refers to the design philosophy of the processor. Instead of providing a lot of complex and slow instructions, the designers of the chip decided to go for a smaller instruction set which could be processed faster and increase the number of instructions per kilobyte of code. Unlike many other 32 bit chips which have 32 bit instructions, each SH3 command is specified by a string of 16 bits.  This has the side effect of making each instruction very fast (on the order of 2-4 times faster that similar instructions on other chips at the same clock frequency). One example of this instruction reduction is that the instruction to move a byte from the location four bytes farther in the code into the R1 register would be 1110000100000100. If this seems a bit impractical to program in, it is. No one wants to type in 16 1's and 0's every time they do anything. If one did, even a small program would look like this:
Quote
11011000 00000110 11011001 00000110 11011010 00000111 10011011 00010000 10011100 00010000 10011101 00001101 00101010 11000001 00101001 10110001 00101000 11010001 00000000 00001001 00000000 00001001 00000000 00001011 00000000 00001001 11111111 11111111 11111111 10000000 11111111 11111111 11111111 10000100 11111111 11111111 11111111 10000110 00000000 00001001 00000011 00000000 01011010 01111010 10100101 01100101

If you were wondering, that particular code overclocks the processor. But instead of binary, something called hexadecimal is used. As it turns out, every sequence of four bits can be specified by a single hexadecimal letter.



When you convert the previous binary to hex, it becomes much more manageable:

Quote
D806 D906 DA07 9B10 9C10 9D0D 2AC1 29B1 28D1 0009 0009 000B 0009 FFFF FF80 FFFF FF84 FFFF FF86 0009 0300 5A7A A565

Which would you rather use?

In any case, hex editors, such as my personal recommendation of HxD are available and Binary editors are not.

Each instruction operates based on what are known as registers. There are 16 general purpose registers in the processor, labeled R0 through R15. The first 8, R0 through R7, are switched depending on which privilege mode the processor is operating in at the moment. The last 8, R8 through R15, are the same no matter what mode the processor is in. Of these registers, only two have any uses other than to hold data, R0 and R15. R0 is automatically accessed by some instructions and R15 is used as a stack pointer during exception handling. If there are interrupts present or the processor throws code errors, then R15 will probably be corrupted. There are also two other types of registers, called system registers and control registers. These have their uses, but they can have specific methods for accessing and writing to them may cause unintended effects if not controlled. We'll deal with specific registers as we come to them, but understand that any register that is not in the range R0 through R15 should not be used for holding data.

Now, to program in SH3 at the current period of time, you need to modify an existing add-in program, three of which are available here. I recommend the Conversion add-in because it is the easiest to modify. Remember to fill all of the code (except for the last four bytes) after address 7000 with repeating sequences of 00 09. If you use HxD, you can write the code in another document and copy/paste it into the add-in. After that, you must re-compute the security checksums, but that is beyond the scope of this article and will soon be rendered unnecessary.

Programming:

Since all of the basics are out of the way, we can begin with the programming.

The first thing almost every program needs is for the program to be set to an initial state. This is known as data loading and is very easy on most processors. Unfortunately, it can be royal pain on the SH3 because of its RISC design. When you need to load a byte into a register Rn, you'd write Enii, where n is the number of the register in hex and ii is the byte of immediate data. For example, EA04 would load register R10 with 0x04h. But, wait, R10 is a 32 bit register, right? It can't hold only 8 bits. Well, that's right, but the processor does something known as sign extension where the byte is extended to 31 bits and placed in the register. The highest bit in the register, bit 32 is used to hold the sign of the number. In this case, it would be positive. If the byte were FF, then the register would hold the two's complement of the number, which is a negative number.

If you want to load a larger chunk of data, the method is more complicated. For loading words, the instruction is 9ndd. For longwords, the instruction is Dndd. The dd byte refers to a displacement, which is multiplied by 2 for words and by 4 for longwords. The product is then added to the program counter and the word or longword starting at that location is placed in Rn. If the instruction is loading a word, then it will sign extend the word as with loading bytes. Here's some example code:

Code: (Longword loading example) [Select]
---------------------------
| Address  | Instruction  |
| 0000     | DF01         |
| 0002     | 0009         |
| 0004     | 0009         |
| 0006     | FFFF         |
| 0008     | FF80         |
---------------------------

This will take the longword FFFF FF80 starting at address 0006 and place it in R15.

This will just allow you to load a program from nearby constant locations within the program. It's also important to be able to important data from other places in the calculator. This handled by another set of instructions, 6nm0, 6nm1, and 6nm2. These instructions will move a byte, word, and longword from the address contained in Rm to Rn. Rm is called a pointer because it "points" to the data. In reality, if you were use this for its obvious use of importing sets of continuous data, it'd be less efficient that another routine, but we'll get that more efficient and obfuscated method to that later.

So, let's write a program to import some data about the processor from the FRQCR register, which controls the processor clocks.

Code: [Select]
0009     // This just increments the program counter without doing anything.
D803     // Load the address of FRQCR (FFFF FF80)
6980     // Load R9 with the first byte of FRQCR
7801     // Increment the pointer R8
6A80     // Load R10 with the second byte of FRQCR
000B     // Return from the program. Always include this and always have 00 09 after it.
0009
0009
FFFF
FF80

If you couldn't tell, this program looks up the address of the FRQCR register (which is a 16 bit control register, hence the two byte imports) and then uses that address as a pointer in the other commands. Now you could use move a word into R9 and be done  faster, but that's beside the point. As you can see, importing data is a pain, but it's possible.

Arithmetic:

Now that you can move data around, you'll at some point want to work with it. This is often done with the arithmetic commands.

Even though the SH3 CPU is a RISC processor, it still has enough math commands to make it usable. Most of these are variations on standard addition, subtraction, multiplication and division. For example, let's say you were using R1 as a counter variable and you wanted it to increment during a loop. An easy, efficient way to do this would be 7101, which adds the quantity $01 to R1. However, this does not register an overflow or carry in the register.

More later

« Last Edit: February 11, 2011, 06:02:57 pm by Qwerty.55 »
∂²Ψ    -(2m(V(x)-E)Ψ
---  = -------------
∂x²        ℏ²Ψ

Offline JosJuice

  • LV10 31337 u53r (Next: 2000)
  • **********
  • Posts: 1344
  • Rating: +66/-14
    • View Profile
Re: SH3 programming tutorials
« Reply #1 on: January 29, 2011, 05:07:09 am »
:w00t:

Awesome... This really helps me. Now I just need to get a Prizm, and then I can start hacking too :P

Offline jnesselr

  • King Graphmastur
  • LV11 Super Veteran (Next: 3000)
  • ***********
  • Posts: 2270
  • Rating: +81/-20
  • TAO == epic
    • View Profile
Re: SH3 programming tutorials
« Reply #2 on: January 29, 2011, 08:49:49 am »
Why are security bytes going to be rendered useless? And why 00 09 filling.

Offline JosJuice

  • LV10 31337 u53r (Next: 2000)
  • **********
  • Posts: 1344
  • Rating: +66/-14
    • View Profile
Re: SH3 programming tutorials
« Reply #3 on: January 29, 2011, 09:07:42 am »
Why are security bytes going to be rendered useless? And why 00 09 filling.
00 09 is NOP. Replacing everything with NOP is easier than making the file smaller.

Offline jnesselr

  • King Graphmastur
  • LV11 Super Veteran (Next: 3000)
  • ***********
  • Posts: 2270
  • Rating: +81/-20
  • TAO == epic
    • View Profile
Re: SH3 programming tutorials
« Reply #4 on: January 29, 2011, 09:50:53 am »
Why are security bytes going to be rendered useless? And why 00 09 filling.
00 09 is NOP. Replacing everything with NOP is easier than making the file smaller.
Oh, okay, that makes sense. I'm assuming it only takes one instruction cycle as well. ;-)

Offline AngelFish

  • Is this my custom title?
  • Administrator
  • LV12 Extreme Poster (Next: 5000)
  • ************
  • Posts: 3242
  • Rating: +270/-27
  • I'm a Fishbot
    • View Profile
Re: SH3 programming tutorials
« Reply #5 on: January 29, 2011, 04:00:28 pm »
Why are security bytes going to be rendered useless?

z80 man is writing a program to compute them automatically, so there's really no need to compute the difference in checksums, then divide it by FF, change n bytes, then take the answer mod FF and change the final byte accordingly.

EDIT: Stuff added to the tutorial.
∂²Ψ    -(2m(V(x)-E)Ψ
---  = -------------
∂x²        ℏ²Ψ

Offline DJ Omnimaga

  • Now active at https://codewalr.us
  • CoT Emeritus
  • LV15 Omnimagician (Next: --)
  • *
  • Posts: 55821
  • Rating: +3151/-232
  • CodeWalrus founder & retired Omnimaga founder
    • View Profile
    • DJ Omnimaga Music
Re: SH3 programming tutorials
« Reply #6 on: January 30, 2011, 08:13:41 pm »
Hopefully that should help people get involved in Prizm stuff. I personally will probably not start SH3 ASM now, though, because I'm more a high level language person.
In case you are wondering where I went, I left Omni back in 2015 to form CodeWalrus due to various reasons explained back then, but I stopped calc dev in 2016 and am now mostly active on the CW Discord server at https://discord.gg/cuZcfcF


Bandcamp|Reverbnation|Facebook|Youtube|Twitter

Offline Munchor

  • LV13 Extreme Addict (Next: 9001)
  • *************
  • Posts: 6199
  • Rating: +295/-121
  • Code Recycler
    • View Profile
Re: SH3 programming tutorials
« Reply #7 on: January 31, 2011, 08:51:45 am »
:w00t: This is great, I can make some progress in PRIZM Disassembler now!

Offline JosJuice

  • LV10 31337 u53r (Next: 2000)
  • **********
  • Posts: 1344
  • Rating: +66/-14
    • View Profile
Re: SH3 programming tutorials
« Reply #8 on: February 22, 2011, 12:52:08 pm »
I re-read this tutorial to learn more, and I noticed that there's something I can't understand - the sign extension that is performed when moving a 8-bit or 16-bit value to a register. How does it work?

Offline z80man

  • Casio Traitor
  • LV8 Addict (Next: 1000)
  • ********
  • Posts: 977
  • Rating: +85/-3
    • View Profile
Re: SH3 programming tutorials
« Reply #9 on: February 22, 2011, 11:16:18 pm »
I re-read this tutorial to learn more, and I noticed that there's something I can't understand - the sign extension that is performed when moving a 8-bit or 16-bit value to a register. How does it work?
As best as I can remember sign exentension means that F's are placed in front of the data. So for example if you load the byte $D5 into R0 for example. R0 will then read $FFFFFFD5.
« Last Edit: February 22, 2011, 11:18:18 pm by z80man »

List of stuff I need to do before September:
1. Finish the Emulator of the Casio Prizm (in active development)
2. Finish the the SH3 asm IDE/assembler/linker program (in active development)
3. Create a partial Java virtual machine  for the Prizm (not started)
4. Create Axe for the Prizm with an Axe legacy mode (in planning phase)
5. Develop a large set of C and asm libraries for the Prizm (some progress)
6. Create an emulator of the 83+ for the Prizm (not started)
7. Create a well polished game that showcases the ability of the Casio Prizm (not started)

Offline Eiyeron

  • Urist McEiyolobster
  • LV10 31337 u53r (Next: 2000)
  • **********
  • Posts: 1430
  • Rating: +130/-10
  • (-_(//));
    • View Profile
    • Rétro-Actif : Rétro/Prog/Blog
Re: SH3 programming tutorials
« Reply #10 on: August 29, 2012, 03:46:57 pm »
Bump, I would like some tutorials to begin SH3's Asm...