Sega SC-3000 emulated on a PropC3 equipped with a C3Synapse
Hi to everybody!
Here it is the first video of the early working version of the Sega SC-3000 emulator for the PropC3+C3Synapse.
This video shows the Parallax PropC3 unit, equipped with a C3Synapse memory expansion, that run the Sega SC-3000 emulator for Propeller. As I have said, this is an early version of the emulator and still needs to be bug fixed but, it can run already many games such as: Exerion, Champion boxing, Ninja princess, Pacar, Star force and many others.
( Sorry for the quality of the video, my camera really as to be changed!
)
The Sega SC-3000 system is emulated by the Propeller present in the PropC3 unit and uses the memory space on the expansion memory C3Synapse for the Z80 emulation. In addiction I have built a simple parallel Joystick interface that uses the free IO pins left on the PropC3 unit.
The emulator is configured as follows:
Z80:
This is the customized version of the zq80 program by Pulmoll http://forums.parallax.com/showthread.php?121579-qZ80-the-third-shot&highlight=qz80
The Z80 uses the SRAM memory space of the C3Synapse as fast as possible. In order to do that I had to make a customized version of the qz80 by Pullmoll.
VDP:
This is the TMS9928 driver that I'm developing. The actual version is completely in ASM and has the IO interface embedded. It uses 16Kb of shared memory as fast VRAM.
PSG:
This is the SN76489 driver used by my VGM music player. It has been updated and the length of the code reduced to save memory for the emulator. Actually there is the stable version of the SN76489 available in the OBEX section of the Parallax site: http://obex.parallax.com/objects/891/
The memory mapper program actually is really simple and has is embedded in each emulated chip. It has to be improved.
In the future I will embed a boot loader in the emulator, something like that shown in this video that I made some time ago:
http://www.youtube.com/watch?v=0q_NxV6mi0I&feature=g-upl
And as soon as I will have a stable alpha version I will start publishing the source code within the Parallax forum.
Enjoy for now!
Here it is the first video of the early working version of the Sega SC-3000 emulator for the PropC3+C3Synapse.
This video shows the Parallax PropC3 unit, equipped with a C3Synapse memory expansion, that run the Sega SC-3000 emulator for Propeller. As I have said, this is an early version of the emulator and still needs to be bug fixed but, it can run already many games such as: Exerion, Champion boxing, Ninja princess, Pacar, Star force and many others.
( Sorry for the quality of the video, my camera really as to be changed!
The Sega SC-3000 system is emulated by the Propeller present in the PropC3 unit and uses the memory space on the expansion memory C3Synapse for the Z80 emulation. In addiction I have built a simple parallel Joystick interface that uses the free IO pins left on the PropC3 unit.
The emulator is configured as follows:
Z80:
This is the customized version of the zq80 program by Pulmoll http://forums.parallax.com/showthread.php?121579-qZ80-the-third-shot&highlight=qz80
The Z80 uses the SRAM memory space of the C3Synapse as fast as possible. In order to do that I had to make a customized version of the qz80 by Pullmoll.
VDP:
This is the TMS9928 driver that I'm developing. The actual version is completely in ASM and has the IO interface embedded. It uses 16Kb of shared memory as fast VRAM.
PSG:
This is the SN76489 driver used by my VGM music player. It has been updated and the length of the code reduced to save memory for the emulator. Actually there is the stable version of the SN76489 available in the OBEX section of the Parallax site: http://obex.parallax.com/objects/891/
The memory mapper program actually is really simple and has is embedded in each emulated chip. It has to be improved.
In the future I will embed a boot loader in the emulator, something like that shown in this video that I made some time ago:
http://www.youtube.com/watch?v=0q_NxV6mi0I&feature=g-upl
And as soon as I will have a stable alpha version I will start publishing the source code within the Parallax forum.
Enjoy for now!


Comments
Well done Francesco, more emulation for the prop is a good thing...
/Johannes
Looks like I need to get hold of a C3:)
I must admit that I'm going very slow in this project but little by little things seem to work out.
There is still the boot loaded to be done for loading roms into the C3Synapse. Actually the rom loader is run from the ram of the PropC3, then it reboots the C3 that loads the emulator from the eeprom. There is no more space for a boot loader within the emulator since half of the ram is taken by the TMS9928 VRAM ( 16 KB ), and 9,8KB are taken by the qz80.
I had to fit the TMS9928 vdp main driver, the renderer with lookup tables, the tv driver and the SN76489 in almost 6KB, and leave some space for the line buffer and memory maspped registers for the VDP.
Not easy.
I think that If I split the emulator in 2 Propellers, one on the PropC3 and an external one placed on some board on top of the C3Synapse, maybe I can have more chances to complete this emulator, and maybe to have more resources to go beyond with a Sega Master System emulation.
But this is not in my plans right now.
What sort of speed are you getting the Z80 to run. I never got it much over 2Mhz, and the Sega is 3.57Mhz. Are you using a cache for the ram?
I wonder if it would run on a touchscreen? I'm working at the moment on a hybrid propeller and a real Z80 chip as I think there are some clever things we can do.
Are you reusing space in hub ram after a cog is loaded?
Hi guys, thank you very much.
I haven't implemented any cache yet, nor measured speed, nor implemented any "frame skip" method to increase emulation speed like I did for my Flash Action Script emulators. The first goal was to make all games work.
Almost the 75% of the games now work, but I had to reduce the code of the qz80 in some places to save some machine cycles. The actual memory access routines for the C3Synapse are the following:
'*************************************************************************************************************** ' Memory access functions (C3Synapse) '*************************************************************************************************************** ' Pin definitions for accessing the HC138 CMD_DATA_OUT_DIR long 000000_00000111_00000000_11111111 DATA_Reset long 111111_11111111_11111111_00000000 CMD_DATA_Reset long 111111_11111000_11111111_00000000 CMD_Reset long 111111_11111000_11111111_11111111 ' Command for accessing the HC138 WrCmd long 01_00000000_00000000 RdCmd long 10_00000000_00000000 SetMd long 00_00000000_00000000 SetHi long 01_00000000_00000000 SetLo long 10_00000000_00000000 '*************************************************************************************************************** {┌─────────────────────────────────────────────────────────────┐ │ _rd_byte , input: Address, Byte, Num output: void │ └─────────────────────────────────────────────────────────────┘ - Write at arg0 Address, arg2 Num bytes arg1 byte into Synapse - DATA_Reset 111111_11111111_11111111_00000000 CMD_Reset 111111_11111000_11111111_11111111 RdCmd 10_00000000_00000000 Activates line 2 of multiplexer 74HC138 This routine reads a single byte by first setting up the memory address. It performs a "RANDOM ACCESS MEMORY" on Synapse } rd_byte rd_rom rd_ram call #ram_address ' sets up the latches with the correct ram address and dira, DATA_Reset ' setup the input direction for data or outa, RdCmd ' setup the read command mov alu, ina ' get value from the data bus and outa, CMD_Reset ' reset command bits to finalize command or dira, CMD_DATA_OUT_DIR ' set data bus direction back to output and alu, #$ff ' extract 8 bits rd_byte_ret rd_opcode_ret rd_byte_hl_ret ret '*************************************************************************************************************** {┌─────────────────────────────────────────────────────────────┐ │ _wr_byte, input: address word, data byte, output: void │ └─────────────────────────────────────────────────────────────┘ - Write at arg0 Address, a byte arg1 into Synapse - CMD_Reset 111111_11111000_11111111_11111111 WrCmd 01_00000000_00000000 Activates line 1 of multiplexer 74HC138 This routine writes a single byte by first setting up the memory address. It performs a "RANDOM ACCESS MEMORY" on Synapse } wr_byte wr_ram cmp ea, #romlimit wz,wc if_b jmp #wr_byte_ret call #ram_address ' sets up the latches with the correct ram address mov outx, alu ' get the byte to output or outa, outx ' prepare data on the bus or outa, WrCmd ' set command bits to value 1 and outa, CMD_Reset ' reset command bits wr_byte_ret ret romlimit long $C000 '*************************************************************************************************************** {┌─────────────────────────────────────────────────────────────┐ │ _setAddress , input: address word, output: void │ └─────────────────────────────────────────────────────────────┘ - Sets the address to write/read on Synapse - CMD_DATA_Reset 111111_11111000_11111111_00000000 SetMd 00_00000000_00000000 Activates line 4 of multiplexer 74HC138 SetLo 10_00000000_00000000 Activates line 6 of multiplexer 74HC138 The address is set by updating two nibbles of the address ( 64KB of memory space ). Each nibble is updated when its value is sent to Synapse by outputting to the C3 pins 16..18 the proper value, and reset the command with a null value } ram_address mov outx, ea ' get the address into a temp variable and outx, #$FF mov outa, outx ' prepare the value on port or outa, SetLo ' send the command to multiplexer to drive the first latch and outa, CMD_Reset ' reset the data bus mov outx, ea ' get the address into a temp variable shr outx, #8 ' shift right by 8 places mov outa, outx ' prepare the value on port or outa, SetMd ' send the command to multiplexer to drive the second latch and outa, CMD_Reset ' reset the data bus mov outa, #0 ' prepare the value on port, 0 because the address range is up to 64k ram_address_ret ret '***************************************************************************************************************As you can see the access to the C3Synapse is very simple, and here the access to the memory space is from 0 to $FFFF, so I handle only two nibbles of the address. This saves a lot of time. The memory mapper of the SEGA SC-3000 is pretty simple, so I removed all memory access tables and put only one control on the write routine. The SEGA SC-3000 has only 2KB of DRAM mapped from $C000. Considering that the Z80 can handle 64KB a time, and the SEGA SC-3000 only use that memory space, it's useless to update everytime the third nibble of the address.
Some games run almost at the same speed of a real machine, others are really slow, mostly the sprites move very slow, not the background music. That means to me that there's something that can still be done to improve the speed.
Exerion for example, one of the games in the video, is really fast, more than I expected. I still have to see that but I haven't so mush time now. I'm studying something else. But right now I'm really happy and proud of what's coming out from this emulator
Uhm, I'm not sure about that. Now I have only one cog free, so that should drive the touch screen. But, the controls for the SC-3000, I mean in the real machine, it's provided by a Intel8255, so the Z80, when accesses the PPI port expect to read data from the keyboard matrix. This means that the z80 writes one byte to enable one column of the matrix, then it reads 2 bytes from the rows. In the emulated machine I only control the joystick, so I can reduce the code and embed it in the qz80 code:
'*************************************************************************************************************** ' Write a byte from the alu to an I/O port ' wr_port mov io_port, ea cmp io_port, #$7F wz ' the PSG port if_z jmp #wr_port_set cmp io_port, #$BF wz ' the VDP Register port if_z jmp #wr_port_set cmp io_port, #$BE wz ' the VDP Data port if_z jmp #wr_port_set jmp #wr_port_ret wr_port_set mov t1, #1 shl t1, #16 or t1, ea shl t1, #8 or t1, alu wrlong t1, io_command :wait rdlong t1, io_command '...and wait for it to be completed. shr t1, #24 WZ, NR if_nz jmp #:wait wr_port_ret ret '*************************************************************************************************************** ' Read a byte from an I/O port into the alu ' rd_port mov io_port, ea and io_port, #$FF cmp io_port, #$DC wz ' the first PPI port if_z jmp #read_A cmp io_port, #$DD wz ' the second PPI port ( actually not used ) if_z jmp #read_B cmp io_port, #$BF wz ' the VDP Register port if_z jmp #rd_port_set cmp io_port, #$BE wz ' the VDP Data port if_z jmp #rd_port_set jmp #rd_port_ret rd_port_set mov t1, #2 shl t1, #16 or t1, ea shl t1, #8 rd_port_cmd wrlong t1, io_command :wait rdlong t1, io_command shr t1, #24 WZ, NR if_nz jmp #:wait mov alu, t1 and alu, #$ff rd_port_cmd_ret rd_port_ret ret '******************************************* ' Pins checked are from P19 to P22 ( direction buttons ), and P26,P27 ( triggers ) of the PropC3 Bus, the ones you can see from the video read_A andn dira, dirmask mov alu, ina mov movements, alu shr movements, #19 and movements, #%1111 mov triggers, alu shr triggers, #26 and triggers, #%11 shl triggers, #4 or movements, triggers or movements, #%11000000 mov alu, movements jmp #rd_port_ret read_B mov alu, #$FF jmp #rd_port_ret io_port long 0 dirback long 0 dirmask long %00001100_01111000_00000000_00000000 movements long 0 triggers long 0 '***************************************************************************************************************No. The hub ram is full. 16KB used by the TMS9928 as VRAM, and 9,8KB are used by the qz80. The other memory is used by the VDP program code ( driver, renderers and TV driver )
Have you seen the AX81, AX81b and AX82 stuff.