Hello! Newbie question here—I've written homebrew for other systems before, even dual-CPU ones like the DS, but I'm at a loss figuring out where to start with writing a little Sega CD demo that will compile to a bootable ISO (with custom code for both CPUs).
I gave BasiEgaXorz a shot, and that basically worked fine, except that there don't seem to be any functions for accessing the scaling/rotation hardware, or any straightforward way to include your own code for the Sub CPU so as to do it yourself (I thought about including some assembly and then manually copying it into the Sub CPU's program memory at runtime, but that's starting to seem complicated enough that doing it through BasiEgaXorz instead of just through a compiler starts to feel silly)
I guess I'm asking is, are there any good examples or instructions of how to compile to a Mode 2 .iso rather than a cartridge .bin in something like SGDK? (What does the Mode 2 iso format even look like—is it as simple as compiling one cartridge-style ROM for each CPU and then just inserting them at the right offsets into the CD? Or does the bios only load one CPU's code, and then that CPU's program has to manually load the other's, or something?)
Compiling a bootable ISO?
Moderator: Mask of Destiny
-
- Very interested
- Posts: 2984
- Joined: Fri Aug 17, 2007 9:33 pm
Re: Compiling a bootable ISO?
The boot sector of the CD contains the initial program loader, which runs on the MD side. This loads the CD side code, which then controls everything. I've done a few examples you'll find here, including a raycasting demo with MOD music on the PCM chip. There's not any real ASIC drawing examples yet, though I know someone working on one.
Right now, there's nothing like SGDK for the CD, it's a bit more low-level right now, so you need more knowledge of the CD hardware and how it works. You'll want to start by looking at all the various open source CD code, like Frog Feast and my PCM music and raycasting examples.
Right now, there's nothing like SGDK for the CD, it's a bit more low-level right now, so you need more knowledge of the CD hardware and how it works. You'll want to start by looking at all the various open source CD code, like Frog Feast and my PCM music and raycasting examples.
-
- Newbie
- Posts: 3
- Joined: Sun Mar 17, 2019 12:39 am
Re: Compiling a bootable ISO?
OK, that makes sense! So the idea really is basically "compile some assembly, append the result into the initial Main CPU program (as "data"), then DMA it into the Sub CPU's program memory"? (Does the Sub CPU always jump execution to a certain address when it gets back access to its program memory, I guess?)
Having demos to refer to sounds great—where exactly can I find them? I think I've found Frog Feast here: http://frogfeast.rastersoft.net/SegaCDSrc.html but I'm not sure where to find the raycasting+pcm demo (I'm not planning on doing much of either, but more to refer to never hurts!)
Having demos to refer to sounds great—where exactly can I find them? I think I've found Frog Feast here: http://frogfeast.rastersoft.net/SegaCDSrc.html but I'm not sure where to find the raycasting+pcm demo (I'm not planning on doing much of either, but more to refer to never hurts!)
-
- Very interested
- Posts: 2984
- Joined: Fri Aug 17, 2007 9:33 pm
Re: Compiling a bootable ISO?
Well, the cd boot sector is split into two parts - the IPL for the MD side, and then the loader code for the CD side. The MD initial program loader is in the first sector and looks like this
It is a couple headers followed by the security blob which then falls into the main MD loader code. The security blob is checksummed by the CD BIOS, which is why it's incbin'd into the code. This is legal in the US because copyright cannot be used for security, so the binary blob fails as copyrightable content. This was decided when Lexmark lost their case where they tried a similar trick on printer cart heads.
The MD loader must know how the CD loader works - note how I look for bytes in the comm regs. This code should be short - it's for loading code, not being game code. I tend to load the main MD code for a CD game into word ram, then jump to 0x200000. That code copies code to 0xF01000 and then jumps to that, so I'm not actually running from word ram, I run from work ram.
The CD side of the boot sector code looks like this
It's a couple more headers and the main CD function entries: SPInit - called first before the vblank int is enabled; this should initialize the CD state, like the word ram mode and the like. SPMain - the main CD entry point; this should use the BIOS calls to load the game itself. SPInt2 - this is the main vblank int call; when the MD side asserts INT2 inside the MD vblank int code, the CD BIOS calls this function on the CD side. Note that while the MD side COULD assert INT2 any time it feels like, the BIOS assumes that it's only asserted once per vblank. If you don't use the CD BIOS after the game is loaded, you could use this int for other purposes, but that's beyond most examples or games. SPNull - this is a reserved call for the BIOS... I use it in my code to return a global variable table so that I can call pieces of the loader code from the game without worrying about where they are in memory.
After that code, you can have more code used for loading, but it should still be fairly small. I include a routine to call the BIOS to read the CD, and some really low-level ISO9660 helper functions.
Here's an arc of my current cdboot code: http://www.mediafire.com/file/bh3845u25 ... 31.7z/file
That has everything to make a SCD boot sector for any region.
Here's an arc of my MOD player for the SCD: http://www.mediafire.com/file/a46r5bmox ... ds.7z/file
That includes an older version of cdboot, my library for using the SCD PCM chip, and the mod player code. It's also got an ISO of the resulting player with some mods.
Here's the raycasting demo: http://www.mediafire.com/file/tg1hp1wax ... 09.7z/file
Here's a recent set of files to setup my toolchain for MD/CD/32X: http://www.mediafire.com/file/4wt3dym1g ... 7.zip/file
This should give you everything you need to get started, but looking over other code examples is always recommended. If you have questions, just ask. I'm always willing to answer questions if I know the answer, and this gets a bit complicated compared to plain cart games. CD32X added one more layer on top of it all - you load like a normal CD game, look for the 32X, and put a special boot record into the 32X frame buffer to start it up for CD32X games.
And here's an arc of my CD32X MOD player: http://www.mediafire.com/file/dn069tm15 ... 25.7z/file
Not sure if you plan on looking at CD32X, but I might as well stick it here as one more link for anyone looking for my CD/CD32X code.
Good luck!
Code: Select all
| SEGA CD Loader code
| by Chilly Willy
| First part of cdrom header
|
| Main-CPU IP, comprising sectors 0 and 1. Sector 0 is loaded automatically,
| while sector 1 is from the header.
.text
| Standard MegaCD Volume Header at 0x0
.ascii "SEGADISCSYSTEM "
.asciz "CDBOOTLOADR"
.word 0x0100
.word 0x0001
.asciz "SEGACD BOOT"
.word 0x0001
.word 0x0000
.long 0x00000800 /* main 68000 initial program cd offset */
.long 0x00000800 /* main 68000 initial program cd size */
.long 0x00000000 /* main 68000 initial program entry offset */
.long 0x00000000 /* main 68000 initial program Work RAM size */
.long 0x00001000 /* sub 68000 initial program cd offset */
.long 0x00007000 /* sub 68000 initial program cd size */
.long 0x00000000 /* sub 68000 initial program entry offset */
.long 0x00000000 /* sub 68000 initial program Work RAM size */
.ascii "03242013 " /* date - MMDDYYYY */
.space 160, 0x20
| Standard MegaDrive ROM header at 0x100
.ascii "SEGA CD Loader "
.ascii "(C)2013 "
.ascii "CD Boot Loader "
.ascii "CD Boot Loader "
.ascii "GM MK-0000 -00 "
.ascii "J6 " /* controller info */
.ascii " "
.ascii " " /* modem info */
.ascii " "
.ascii "JUE " /* region info */
| Standard MegaCD startup code at 0x200
|
| This data and the code following is copied to the Main-CPU Work RAM at
| 0xFF0000 and run.
.global _start
_start:
.ifdef REGION_US
.incbin "ipl/us.bin", 0, 0x584
.endif
.ifdef REGION_EU
.incbin "ipl/eu.bin", 0, 0x56E
.endif
.ifdef REGION_JP
.incbin "ipl/jp.bin", 0, 0x156
.endif
| fall into startup code for Main-CPU
lea 0xA10000.l,a5
move.b #0,0x200E(a5) /* clear main comm port */
| wait for MD init handshake from CD
0:
cmpi.b #'I,0x200F(a5)
bne.b 0b
bset #1,0x2003(a5) /* give Sub-CPU Word RAM */
move.b #'B,0x200E(a5) /* main comm port - do boot */
| wait for MD code handshake from CD
1:
cmpi.b #'M,0x200F(a5)
bne.b 1b
2:
btst #0,0x2003(a5) /* Main-CPU has Word RAM? */
beq.b 2b
move.w #0x2700,sr
lea 0xFFFD00,a0
movea.l a0,sp
jmp 0x200000.l
The MD loader must know how the CD loader works - note how I look for bytes in the comm regs. This code should be short - it's for loading code, not being game code. I tend to load the main MD code for a CD game into word ram, then jump to 0x200000. That code copies code to 0xF01000 and then jumps to that, so I'm not actually running from word ram, I run from work ram.
The CD side of the boot sector code looks like this
Code: Select all
.org 0x1000
| Second part of cdrom header
|
| Sub-CPU IP, comprising sectors 2 to 15 from the header.
| Standard MegaCD Sub-CPU Program Header at 0x1000 (loaded to 0x6000)
SPHeader:
.asciz "MAIN-SUBCPU"
.word 0x0001,0x0000
.long 0x00000000
.long 0x00000000
.long SPHeaderOffsets-SPHeader
.long 0x00000000
SPHeaderOffsets:
.word SPInit-SPHeaderOffsets
.word SPMain-SPHeaderOffsets
.word SPInt2-SPHeaderOffsets
.word SPNull-SPHeaderOffsets
.word 0x0000
| Sub-CPU Program Initialization (VBlank not enabled yet)
SPInit:
lea GlobalVars(pc),a0
move.l #0,VBLANK_HANDLER(a0) /* clear VBlank handler vector */
lea InitCD(pc),a1
move.l a1,INIT_CD(a0) /* set InitCD vector */
lea ReadCD(pc),a1
move.l a1,READ_CD(a0) /* set ReadCD vector */
lea SetCWD(pc),a1
move.l a1,SET_CWD(a0) /* set SetCWD vector */
lea FirstDirSector(pc),a1
move.l a1,FIRST_DIR_SEC(a0) /* set FirstDirSector vector */
lea NextDirSector(pc),a1
move.l a1,NEXT_DIR_SEC(a0) /* set NextDirSector vector */
lea FindDirEntry(pc),a1
move.l a1,FIND_DIR_ENTRY(a0) /* set FindDirEntry vector */
lea NextDirEntry(pc),a1
move.l a1,NEXT_DIR_ENTRY(a0) /* set NextDirEntry vector */
lea LoadFile(pc),a1
move.l a1,LOAD_FILE(a0) /* set LoadFile vector */
andi.b #0xE2,0x8003.w /* Priority Mode = off, 2M mode */
move.b #'I,0x800F.w /* send MD init handshake to MD */
rts
| Sub-CPU Program Main Entry Point (VBlank now enabled)
SPMain:
lea GlobalVars(pc),a6
lea iso_pvd_magic(pc),a5
bsr InitCD
| wait for boot handshake from main
0:
cmpi.b #'B,0x800E.w
bne.b 0b
move.b #0,0x800F.w /* clear sub comm port */
| read boot file from cd
lea root_dirname(pc),a0
bsr SetCWD /* set current working directory to root */
lea boot_name(pc),a0
lea LOAD_BUFFER.l,a1
bsr LoadFile
bne.b exit /* no Sub-CPU boot file */
1:
lea LOAD_BUFFER.l,a0 /* char *start(void) */
jsr (a0)
| app exited - if we return, the BIOS will call SPMain again
|
| my own idea - return NULL if done, else return a pointer to the filename
| to load and run, thus allowing apps to launch other apps
move.w #0x2700,sr /* disallow interrupts */
lea GlobalVars(pc),a6
clr.l VBLANK_HANDLER(a6)
clr.l VBLANK_PARAM(a6)
move.w #0x2000,sr /* allow interrupts */
tst.l d0
beq exit
move.l d0,-(sp)
andi.b #0xE2,0x8003.w /* Priority Mode = off, 2M mode */
move.b #'I,0x800F.w /* send init - switch Word RAM to Sub-CPU */
2:
cmpi.b #'I,0x800E.w
bne.b 2b /* wait for command ACK */
move.b #0,0x800F.w /* ACK handshake */
lea iso_pvd_magic(pc),a5
bsr InitCD
lea root_dirname(pc),a0
bsr SetCWD /* set current working directory to root */
movea.l (sp)+,a0
lea LOAD_BUFFER.l,a1
bsr LoadFile
beq.b 1b /* load okay */
exit:
rts
| Sub-CPU Program VBlank (INT02) Service Handler
SPInt2:
lea GlobalVars(pc),a0
tst.l VBLANK_HANDLER(a0)
bne.b 0f
rts
0:
move.l VBLANK_PARAM(a0),d0
move.l d0,-(sp)
movea.l VBLANK_HANDLER(a0),a0
jsr (a0)
addq.l #4,sp
rts
| Sub-CPU program Reserved Function - we use it get the loader global vars pointer
SPNull:
lea GlobalVars(pc),a0
move.l a0,d0
rts
After that code, you can have more code used for loading, but it should still be fairly small. I include a routine to call the BIOS to read the CD, and some really low-level ISO9660 helper functions.
Here's an arc of my current cdboot code: http://www.mediafire.com/file/bh3845u25 ... 31.7z/file
That has everything to make a SCD boot sector for any region.
Here's an arc of my MOD player for the SCD: http://www.mediafire.com/file/a46r5bmox ... ds.7z/file
That includes an older version of cdboot, my library for using the SCD PCM chip, and the mod player code. It's also got an ISO of the resulting player with some mods.
Here's the raycasting demo: http://www.mediafire.com/file/tg1hp1wax ... 09.7z/file
Here's a recent set of files to setup my toolchain for MD/CD/32X: http://www.mediafire.com/file/4wt3dym1g ... 7.zip/file
This should give you everything you need to get started, but looking over other code examples is always recommended. If you have questions, just ask. I'm always willing to answer questions if I know the answer, and this gets a bit complicated compared to plain cart games. CD32X added one more layer on top of it all - you load like a normal CD game, look for the 32X, and put a special boot record into the 32X frame buffer to start it up for CD32X games.
And here's an arc of my CD32X MOD player: http://www.mediafire.com/file/dn069tm15 ... 25.7z/file
Not sure if you plan on looking at CD32X, but I might as well stick it here as one more link for anyone looking for my CD/CD32X code.
Good luck!
-
- Newbie
- Posts: 3
- Joined: Sun Mar 17, 2019 12:39 am
Re: Compiling a bootable ISO?
Oh, shoot, so it's actually a little of both then? The BIOS automatically loads some code for both CPUs, but it's limited to a small enough amount that the real bulk of the *actual* program needs to then be loaded BY that initial program? (What is the actual limitation that determines how much can be in these bootstrap programs? I'm making a fairly small demo, I wonder if I can get away with fitting it all in that initial load...)
Thank you for the detailed examples! I'll try and get the toolchain running on my machine, hopefully this'll get me started on the right foot Much appreciated!
Thank you for the detailed examples! I'll try and get the toolchain running on my machine, hopefully this'll get me started on the right foot Much appreciated!
-
- Very interested
- Posts: 2984
- Joined: Fri Aug 17, 2007 9:33 pm
Re: Compiling a bootable ISO?
Yes, exactly.RavenWorks wrote: ↑Sun Mar 24, 2019 2:33 pmOh, shoot, so it's actually a little of both then? The BIOS automatically loads some code for both CPUs, but it's limited to a small enough amount that the real bulk of the *actual* program needs to then be loaded BY that initial program?
The MD side is limited to a max of $1000 minus the size of the headers and the binary security blob. The CD side is limited to $7000 bytes total for the headers and the loader code and functions. Remember, this is the boot sector of an ISO9660 CD, so the limit is 16 sectors, or 32KB ($8000 = $1000 + $7000).What is the actual limitation that determines how much can be in these bootstrap programs? I'm making a fairly small demo, I wonder if I can get away with fitting it all in that initial load...
You're welcome. Note that I do all my deving on Xubuntu, but any linux distro would work, and I know others who have gotten it to work with MinGW or Cygwin.Thank you for the detailed examples! I'll try and get the toolchain running on my machine, hopefully this'll get me started on the right foot Much appreciated!