View Full Version : Want to start programming for the Genesis.
Jadty
05-21-2011, 03:50 PM
What would be the absolute first I'd need to check if I wanted to learn programming on the Genesis? I have no background in programming at all, but I'm interested in learning the Genesis and maybe the 32X.
Where can I find a comprehensive, easy to learn SDK? Tutorials on the 68K language? Let's see if the programming experts on the site can help me on this one and guide me through the right path.
Kamahl
05-21-2011, 04:36 PM
Learn how to program in C.
Then understand how the genesis hardware works.
Then use this:
http://code.google.com/p/sgdk/w/list
Jadty
05-21-2011, 07:21 PM
Learn how to program in C.
Then understand how the genesis hardware works.
Then use this:
http://code.google.com/p/sgdk/w/list
I'm downloading the Code::Blocks application and reading a tutorial on C. I've seen a few comments here stating that Assembler IS the language of choice when programming for the Genesis. What can you tell me about this? I want to take the easiest, most efficient route to learning.
Kamahl
05-21-2011, 07:40 PM
I'm downloading the Code::Blocks application and reading a tutorial on C. I've seen a few comments here stating that Assembler IS the language of choice when programming for the Genesis. What can you tell me about this? I want to take the easiest, most efficient route to learning.
Assembly is a bit too much for most people, without knowing at least some basics on programming you probably wouldn't get very far with it. I really recommend starting out using C and the Sega Genesis Dev Kit.
Pier Solar was programmed in C so... I'd say it's enough ;)
CurlyPaul
05-21-2011, 07:45 PM
Yeah Assembler is not really the best place to learn programming. Better to learn how to program first and then learn Assembler later. Start with C - its pretty much human readable but gives you enough low level control to get a grib with what you are doing when you get to Assembler
Chilly Willy
05-21-2011, 08:46 PM
Learn C and C++, then there are several threads in this forum that will get you started setting up a toolchain to make C/C++ MD/32X games as well as several demos and games that for the MD/32X that come with the source.
sega16
05-21-2011, 08:57 PM
Learn how to program in C.
Then understand how the genesis hardware works.
Then use this:
http://code.google.com/p/sgdk/w/list
I would highly recommend that you use c with sgdk library.I started with basiegaxorz but then realized that c is more valuable to learn and also sgdk supports fixed point math (decimals on sega genesis :) ) and is generally more powerful than basic.
I am currently re-making my game about dr.robotnik in c and believe me it is gonna be a-lot better than the basic build.
Jadty
05-21-2011, 09:34 PM
Assembly is a bit too much for most people, without knowing at least some basics on programming you probably wouldn't get very far with it. I really recommend starting out using C and the Sega Genesis Dev Kit.
Pier Solar was programmed in C so... I'd say it's enough ;)
Well, if Pier Solar was programmed completely in C (with the additions of the separate Tiido's Sound Engine) I'd say it should be quite enough for me :D
Learn C and C++, then there are several threads in this forum that will get you started setting up a toolchain to make C/C++ MD/32X games as well as several demos and games that for the MD/32X that come with the source.
Appreciate the input from such an expert, too.
I've seen a few comments here stating that Assembler IS the language of choice when programming for the Genesis. What can you tell me about this?I wish it was the case, GCC is horrible at optimizing 68000 assembler making C code really slow and bloated for what it could have been (once I tried it, and the result is that it generated completely invalid code which I have absolutely no idea how it reached that point... and I was just trying to write a vsync function).
I want to take the easiest, most efficient route to learning.Then start making games for computers, not for the Mega Drive, there are many more tools available that will make your life easier while you're learning. In fact, don't attempt to start writing games until you know a programming language very well (at least learn functions and arrays, or whatever is the equivalent in whatever language you use).
On that note, C is still usable on computers (almost everybody will frown upon you for not using a modern language, but whatever, technically you can do everything with C still =P).
Chilly Willy
05-21-2011, 10:38 PM
I wish it was the case, GCC is horrible at optimizing 68000 assembler making C code really slow and bloated for what it could have been (once I tried it, and the result is that it generated completely invalid code which I have absolutely no idea how it reached that point... and I was just trying to write a vsync function).
Two things here to remember: use volatile pointers for hardware access; use optimization level 1 (-O1) for files that access hardware. If you don't do BOTH of those, you're likely to see bad behavior with routines that try to access the hardware. That even gets me now and then... I'll forget and set the file to compile at -O3 with the rest of the game code and it'll fail (to flip buffers, set colors, etc). My thread on making a makefile talks about that, showing how to set one specific file to compile at a different O level than the rest.
On that note, C is still usable on computers (almost everybody will frown upon you for not using a modern language, but whatever, technically you can do everything with C still =P).
I much prefer C to C++ or most other "modern" languages. I only work in C++ if I'm working on someone else's project. My own projects are always C and/or assembly.
Two things here to remember: use volatile pointers for hardware access; use optimization level 1 (-O1) for files that access hardware. If you don't do BOTH of those, you're likely to see bad behavior with routines that try to access the hardware. That even gets me now and then... I'll forget and set the file to compile at -O3 with the rest of the game code and it'll fail (to flip buffers, set colors, etc). My thread on making a makefile talks about that, showing how to set one specific file to compile at a different O level than the rest.By completely wrong code I mean "WTF is GCC trying to do?!". First of all, it was declared volatile uint8_t* const (as a local variable), meaning it's literally a raw pointer =| Second, just look at yourself:
!(*vdpctrl & 0x0008)Somehow GCC did something like this:
move.w (a0), d0
move.w d0, a1
btst.l #3, d1How do you justify that?
And if GCC can't generate proper code with -O3, then GCC is buggy, period. And I've tried with -O1, and while it generates valid code, it also generates too many dummy instructions that most programmers would have spotted out immediately (like moving data around registers for no real reason), and we aren't talking about a platform where wasting cycles is a good idea.
I much prefer C to C++ or most other "modern" languages. I only work in C++ if I'm working on someone else's project. My own projects are always C and/or assembly.By frowning for not using modern languages I mean the newest languages. C is considered too old by most programmers these days. In fact there are some programmers that consider C++ to be too outdated already (especially when it comes to memory management and the like). Beware of those, they will try to convince you that using anything lower level than C# is a waste of time =P
Chilly Willy
05-22-2011, 12:53 AM
By completely wrong code I mean "WTF is GCC trying to do?!". First of all, it was declared volatile uint8_t* const (as a local variable), meaning it's literally a raw pointer =| Second, just look at yourself:
!(*vdpctrl & 0x0008)Somehow GCC did something like this:
move.w (a0), d0
move.w d0, a1
btst.l #3, d1How do you justify that?
First, const means the variable is in a read-only section. Second, what version of gcc? A few OLD versions had some known bugs. If it's a newer version, I'd need to see the C and the asm generated.
And if GCC can't generate proper code with -O3, then GCC is buggy, period. And I've tried with -O1, and while it generates valid code, it also generates too many dummy instructions that most programmers would have spotted out immediately (like moving data around registers for no real reason), and we aren't talking about a platform where wasting cycles is a good idea.
It's not a bug, it's feature of the higher optimization levels that you implicitly agree to when you set the optimization level. Because hardware might not work with those optimization assumptions, you can also set the optimization on a function to function basis via the function attritbutes if you don't want to make a whole file -O1. I just find it easier to put hardware code in a separate file than to set individual function optimization levels.
This is all stuff that the programmer should know and is their responsibility. It's why programming is not for just anyone.
First, const means the variable is in a read-only section. Second, what version of gcc? A few OLD versions had some known bugs. If it's a newer version, I'd need to see the C and the asm generated.The version that comes with the Everdrive devkit, IIRC. Also the const there makes the pointer itself constant (though not the value of what it points, to make that constant it'd need a const before the type). In either case it doesn't justify GCC doing that stupid thing...
It's not a bug, it's feature of the higher optimization levels that you implicitly agree to when you set the optimization level. Because hardware might not work with those optimization assumptions, you can also set the optimization on a function to function basis via the function attritbutes if you don't want to make a whole file -O1. I just find it easier to put hardware code in a separate file than to set individual function optimization levels.Did you completely miss the code I posted? One thing is generating code that makes assumptions that don't work with hardware (like writing words when only bytes work or vice versa), another thing is generating code that is just plain wrong. It reads from the port and stores it in a register, but tests its value from another register. For the record, before this loop there was a similar one (just without the ! operator), and that one was generated right.
Chilly Willy
05-26-2011, 07:11 PM
Did you completely miss the code I posted?
You haven't posted a bit of code... just a line you say is like what you use, and a couple more lines of disassembly you say you THINK you remember it being similar to. Post the actual code and the actual disassembly and maybe I might believe it. Until then, it's more likely you simply don't understand how optimizations work. Read up on memory barriers at least.
void vsync(void) {
volatile uint16_t* const vdpctrl = (uint16_t*) 0xC00004;
while (*vdpctrl & 0x0008);
while (!(*vdpctrl & 0x0008));
}Tell me what's wrong with that code.
And you're overestimating GCC. The mere fact that it tried to read from memory and test the value in the register instead of testing the bit off memory directly means the optimizer isn't doing a very good job. And the piece of assembly I posted wouldn't have worked even if it was standard memory instead of hardware.
EDIT: also, -O3 is labeled as "may generate bloat", not as "may break things". If code works in -O1 but doesn't in -O3 and it's standards compliant, then it's a bug in the compiler. And obviously the volatile qualifier is working here because GCC isn't trying to cache the value in a register.
EDIT 2: also, I said "similar to" because I used asm68k syntax instead of GAS syntax. The generated code was exactly like that.
Chilly Willy
05-26-2011, 10:13 PM
void vsync(void) {
volatile uint16_t* const vdpctrl = (uint16_t*) 0xC00004;
while (*vdpctrl & 0x0008);
while (!(*vdpctrl & 0x0008));
}Tell me what's wrong with that code.
It looks fine... as long as you don't exceed -O1. Only certain platforms retain the access order for volatiles above level 1, the x86 being the only one I'm familiar with. I know for a fact that SuperH, MIPS, and M68K all allow volatiles to be reordered as to access above level 1. A quick google on the issue shows a half decade long argument over what SHOULD happen, but in the end, you're cautioned to not assume volatiles won't be reordered. Linux hardware drivers have special macros you use to access IO, and while x86 merely does a volatile access, other platforms may/do not.
And you're overestimating GCC. The mere fact that it tried to read from memory and test the value in the register instead of testing the bit off memory directly means the optimizer isn't doing a very good job. And the piece of assembly I posted wouldn't have worked even if it was standard memory instead of hardware.
EDIT: also, -O3 is labeled as "may generate bloat", not as "may break things". If code works in -O1 but doesn't in -O3 and it's standards compliant, then it's a bug in the compiler. And obviously the volatile qualifier is working here because GCC isn't trying to cache the value in a register.
I see you're on THAT side of the argument. ;) :D
I'm not going to argue about what it SHOULD do since that has been debated for over five years without resolution, I'm just telling you the way it is. On SH, MIPS, and M68K, you cannot use more than -O1 for hardware references, even with volatile. I'm not sure about ARM... haven't done enough programming on it yet.
EDIT 2: also, I said "similar to" because I used asm68k syntax instead of GAS syntax. The generated code was exactly like that.
I'm not sure I can believe that without seeing it for myself. While no compiler is bug-free, I think something THAT bad would have been noticed right off. Perhaps you should use my guide to make a nice new 4.5.2 gcc toolchain just to be certain.
Sorry if I sound argumentative... this is just something I had to deal with a number of times about an issue that people really get worked up about. I agree that making volatiles synchronous is OBVIOUSLY best, but the devs who work on gcc (at least the non-x86 branches) don't agree. I doubt another five years will resolve the issue, so until then, use -O1 for the file in the makefile, or use the new function attribute to set the opt level to 1 to avoid the issue.
It looks fine... as long as you don't exceed -O1.If it works in -O1 then it should work in -O3, period. It shouldn't change the program behavior at all except for performance. If the program output changes, then the compiler is doing something wrong. For the record, this very same reason is why floating point operations can't be optimized (since that'd change the rounding errors).
Only certain platforms retain the access order for volatiles above level 1, the x86 being the only one I'm familiar with. I know for a fact that SuperH, MIPS, and M68K all allow volatiles to be reordered as to access above level 1. A quick google on the issue shows a half decade long argument over what SHOULD happen, but in the end, you're cautioned to not assume volatiles won't be reordered. Linux hardware drivers have special macros you use to access IO, and while x86 merely does a volatile access, other platforms may/do not.If we're going to argue that volatile accesses can be reordered, then we shouldn't trust them even with -O1, since -O1 still does some optimizations that could potentially lead to a change like that. Only -O0 should be trusted then.
For the record, the whole volatile reordering thing is really more an issue of modern processors, since they usually run instructions in a different order from the program code and then there's the cache hardware deciding to flush memory in completely random orders (it may even decide to not flush writes to memory for looooooong periods of time). The 68000 is not such a processor, though.
Note that the above also means that keeping volatile accesses in order on x86 is completely useless, since the x86 family is probably the worst offender when it comes to internal reordering. The only reason for keeping such a thing is not breaking old PC programs that relied on such accesses.
I'm not going to argue about what it SHOULD do since that has been debated for over five years without resolution, I'm just telling you the way it is. On SH, MIPS, and M68K, you cannot use more than -O1 for hardware references, even with volatile. I'm not sure about ARM... haven't done enough programming on it yet.This is an issue with the compiler, not with the processor. Remember, GCC doesn't have a dedicated compiler for each processor, it runs the same compiler and uses the RTL to generate assembly instructions. This means that GCC will attempt to apply the same kind of optimizations regardless of processors, and this includes reordering instructions.
For the record, this probably means that newer versions of GCC suck completely with old processors, since they have been optimized to deal with modern hardware and the requirements are completely different. Bear into mind that the -march option only limits the opcodes GCC can use, it doesn't change the optimization strategy.
I doubt another five years will resolve the issue, so until then, use -O1 for the file in the makefile, or use the new function attribute to set the opt level to 1 to avoid the issue.I'd say that five years will most likely make the situation worse, not better, since GCC will be even more optimized for modern hardware than for old hardware like the 68000.
Chilly Willy
05-26-2011, 10:34 PM
I did some tests with the 4.5.2 m68k gcc on the code you posted... here's the exact code tested:
#include <stdint.h>
void vsync(void) {
volatile uint16_t* const vdpctrl = (uint16_t*) 0xC00004;
while (*vdpctrl & 0x0008);
while (!(*vdpctrl & 0x0008));
}
and here is the code generated for -O1, -O2, and -O3 respectively:
#NO_APP
.file "test.c"
.text
.align 2
.globl vsync
.type vsync, @function
vsync:
link.w %fp,#0
.L2:
move.w 12582916,%d0
btst #3,%d0
jne .L2
.L4:
move.w 12582916,%d0
btst #3,%d0
jeq .L4
unlk %fp
rts
.size vsync, .-vsync
.ident "GCC: (GNU) 4.5.2"
#NO_APP
.file "test.c"
.text
.align 2
.globl vsync
.type vsync, @function
vsync:
link.w %fp,#0
.L2:
move.w 12582916,%d0
btst #3,%d0
jne .L2
.L4:
move.w 12582916,%d0
btst #3,%d0
jeq .L4
unlk %fp
rts
.size vsync, .-vsync
.ident "GCC: (GNU) 4.5.2"
#NO_APP
.file "test.c"
.text
.align 2
.globl vsync
.type vsync, @function
vsync:
link.w %fp,#0
.L2:
move.w 12582916,%d0
btst #3,%d0
jne .L2
.L4:
move.w 12582916,%d0
btst #3,%d0
jeq .L4
unlk %fp
rts
.size vsync, .-vsync
.ident "GCC: (GNU) 4.5.2"
In this case, the function is so simple that it can't be optimized any more by gcc. It also looks like good code... nothing funky going on. Well, ONE optimization I could see - not using the frame pointer... adding -fomit-frame-pointer to the compile options gives:
#NO_APP
.file "test.c"
.text
.align 2
.globl vsync
.type vsync, @function
vsync:
.L2:
move.w 12582916,%d0
btst #3,%d0
jne .L2
.L4:
move.w 12582916,%d0
btst #3,%d0
jeq .L4
rts
.size vsync, .-vsync
.ident "GCC: (GNU) 4.5.2"
Then the GCC build that comes with the Everdrive devkit is buggy. In any case it goes to show the C code wasn't wrong =|
EDIT: and there's one more optimization that could be done, performing btst directly on memory instead of reading the word into a register. Doing a btst from memory is just a read so the end result should be the same. And yes, it works on real hardware with the VDP.
EDIT 2: and keeping the pointer into a register so it doesn't have to be fetched every time... Either way it's still valid code, I guess.
Chilly Willy
05-26-2011, 11:20 PM
Then the GCC build that comes with the Everdrive devkit is buggy. In any case it goes to show the C code wasn't wrong =|
Yes, the code was fine. :)
EDIT: and there's one more optimization that could be done, performing btst directly on memory instead of reading the word into a register. Doing a btst from memory is just a read so the end result should be the same. And yes, it works on real hardware with the VDP.
EDIT 2: and keeping the pointer into a register so it doesn't have to be fetched every time... Either way it's still valid code, I guess.
Which is why hand-tuned assembly is usually better than the best compiler with optimization generates. The pointer itself was a constant, so loading it once is clearly okay. You have two address registers free as scratch (a0 and a1). The address register indirect fetch (a0) is faster than absolute direct long ADDR.l, and takes four less bytes to encode, so you want to use that when you can. As to using the btst on the address directly, you just need to remember that it works on a byte only, so in the case of the above, you'd need to btst the address + 1. Because the volatile value was defined as a word, it fetched it before doing the test; I'd bet if it had been defined as a byte, it might have just done the btst on the address directly.
The address one could have been detected this way: Check if a given constant pointer is used more than once Check if there's a free address register that can be used Store pointer in register if both of the above are metTo be fair I'm surprised GCC did not find out that one... Maybe the volatile is getting in the way? What happens if the const is removed? Moreover, what happens if it's explicitly marked as register? (one of the rare situations where you'd actually want to use the register keyword, I guess)
As to using the btst on the address directly, you just need to remember that it works on a byte only, so in the case of the above, you'd need to btst the address + 1. Because the volatile value was defined as a word, it fetched it before doing the test; I'd bet if it had been defined as a byte, it might have just done the btst on the address directly.Oh, screw that, with some hardware that could have been an issue. Gah.
Chilly Willy
05-27-2011, 05:27 PM
The address one could have been detected this way: Check if a given constant pointer is used more than once Check if there's a free address register that can be used Store pointer in register if both of the above are metTo be fair I'm surprised GCC did not find out that one... Maybe the volatile is getting in the way? What happens if the const is removed? Moreover, what happens if it's explicitly marked as register? (one of the rare situations where you'd actually want to use the register keyword, I guess)
Taking out const and/or adding register did nothing to the code. Using -Os gives this:
#NO_APP
.file "test.c"
.text
.align 2
.globl vsync
.type vsync, @function
vsync:
.L2:
move.w 12582916,%d0
move.w %d0,%ccr
jmi .L2
.L4:
move.w 12582916,%d0
move.w %d0,%ccr
jpl .L4
rts
.size vsync, .-vsync
.ident "GCC: (GNU) 4.5.2"
If you want UNOPTIMIZED code, try this... this is -O0 code:
#NO_APP
.file "test.c"
.text
.align 2
.globl vsync
.type vsync, @function
vsync:
subq.l #4,%sp
move.l #12582916,(%sp)
nop
.L2:
move.l (%sp),%d0
move.l %d0,%a0
move.w (%a0),%d0
move.w %d0,%d0
and.l #65535,%d0
moveq #8,%d1
and.l %d1,%d0
tst.l %d0
jne .L2
nop
.L3:
move.l (%sp),%d0
move.l %d0,%a0
move.w (%a0),%d0
move.w %d0,%d0
and.l #65535,%d0
moveq #8,%d1
and.l %d1,%d0
tst.l %d0
jeq .L3
addq.l #4,%sp
rts
.size vsync, .-vsync
.ident "GCC: (GNU) 4.5.2"
Oh, screw that, with some hardware that could have been an issue. Gah.
Which is why I mentioned it. Then there's the "fun" of the clr instruction... it does a read before writing 0. Why? No one knows, but that's why you usually see move.w #0,HW_ADDR instead of clr.w HW_ADDR.
I don't feel like making an one-liner, but...
move.w %d0,%ccro_O
EDIT: shouldn't that be move byte instead of move word? CCR is the low byte of SR, and moving a word would mean overwriting the IRQ masks and such.
Chilly Willy
05-27-2011, 08:14 PM
shouldn't that be move byte instead of move word? CCR is the low byte of SR, and moving a word would mean overwriting the IRQ masks and such.
Oddly enough, move to ccr is a WORD operation. Only the immediate logical ops on ccr are bytes, but immediate bytes are stored in the instruction as a word where the upper byte is ignored. When you move a word to the ccr, the upper byte is ignored. When you move FROM ccr, it is also a word with the upper byte set to 0.
Only move to sr sets the upper part of sr. That instruction was made supervisor state only in the 68010 to help with user/super protection. Move from sr is the only way to see the upper part of sr, and was also made supervisor only in the 68010.
For having tested GCC 68k elf a lot i can say that indeed the compiler is not that good for optimization...
Later version are even worst because of modern CPU architecture optimizations which are totally wrong with olders CPU i guess.
It's why i am still distributing GCC 3.4.6 with SGDK... which is a pity as it is not able to inline functions correctly :-/
I never meet optimizations problems with GCC 3.4.6 nor GCC 4.1.X whatever is the optimization level but -O1 generally produces the best code for m68k-elf target :)
If you got problems with your GCC it's probably because you used the (totally) broken SGCC compiler or XGCC which is not very reliable too.
When it comes to ASM versus C, of course ASM could always be faster but GCC with -O1 produces enough good code for me and we keep readability. I use ASM only when GCC is very inefficient and when i need speed.
Kamahl
07-03-2012, 05:33 PM
You could also try this Stef:
http://www.compilers.de/vbcc.html
I'm using it for the Amiga and it's excellent.
That looks interesting ! I wonder how difficult it could be to integrate the compiler in SGDK, it looks like i have to compile it at least and i'm not sure about the needed work for basic C lib (normally i can avoid them as i reimplemented almost all basics methods in SGDK).
Kamahl
07-06-2012, 06:39 PM
That looks interesting ! I wonder how difficult it could be to integrate the compiler in SGDK, it looks like i have to compile it at least and i'm not sure about the needed work for basic C lib (normally i can avoid them as i reimplemented almost all basics methods in SGDK).
Can't help you with that I'm afraid. Shouldn't be too difficult, it's designed to support embedded systems.
See: http://www.ibaug.de/vbcc/doc/vbcc_10.html#SEC109
Chilly Willy
07-06-2012, 11:28 PM
Vbcc is nice, but you need to be aware that it cannot be used for commercial programs without specific permission from the author.
Kamahl
07-07-2012, 09:04 AM
Vbcc is nice, but you need to be aware that it cannot be used for commercial programs without specific permission from the author.
As far as I understood that only applied to using VBCC itself in some commercial software, not compiling one. As long as SGDK remains open source it's all good, even if someone wants to make Pier Solar 2.
The license really isn't clear on this though. That just makes it easier to break in case that's what he really means :p.
Powered by vBulletin® Version 4.2.0 Copyright © 2013 vBulletin Solutions, Inc. All rights reserved.