From MSX to CPC 101

Started by SyX, 22:50, 12 April 14

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.


After the interest about the MSX in the Penguin Wars thread, i have been asked for more information about how to convert a game from MSX.

I could start for making a page in the wiki, but i feel that it would better discuss first here and then i'm sure than with the feedback of the community we will make a much better wiki entry.

As i told in that thread, we are going to port a typical 8-32 KBs japanese cartridge game for MSX 1, because japanese coders always follow the official documents (there is a funny anecdote about the lack of sound in the first japanese games for the c64, it looks the sid specs document was wrong, but as these people wrote the code following the specs, well those games produced sound that it couldn't hear :P).

Bigger cartridges would need more patches, because all of them use a mapper chip to page the extra rom pages. An MSX machine is more flexible in ROM/RAM paging that our CPCs, they can put ROM or RAM in any of the 16 KBs pages the z80 can see; only a CPC+ cartridge can do something similar to this; but there is cartridges, that can use 8 KBs pages, and even a CPC+ can not help here. And companies like Konami added extra sound chips at their best cartridge games (although CTC-AY could help with this, jejeje).

And only MSX 1 machines following the standard. The standard defines 16 KBs of RAM + 16 KBs of VRAM, but thanks to Casio for its PV-7, a machine with only 8 KBs of RAM ($E000-$FFFF), that was supported by all the japanese cartridge games, and because that we are winning 8 KBs of RAM for making CPC things :)

Why do we not port games of MSX2 or superior? Well, the MSX 2 standard defines the machines can have 64 KBs of RAM and 64 KBs of VRAM as minimum (although a very typical machine has 128 KBs of each) and the graphic chip is more advanced (palette of 512 colours, a graphic mode of 256 colours, a mini-blitter, h-blank interrupts, ...). MSX2+ and Turbo R machines have faster cpus, screen modes with 19.000 colours, extra sound chips, ...   

This is happening in real time, less 7 hours to take the plane to Spain for seeing the family during Easter... and bring my CPCs to Brazil, of course :P

Time to start!!! :)

If i remember well, the first Namco games were a good example, i'm going to choose... Tank Battalion, typical 8 KBs cart from 1984.

I'm going to use as hardware reference the nocash portar document and for the bios this page of the MSX Assembly page.

Our task is found how the game access to the VDP (video chip), keyboard and the psg for replacing these routines for ours using the CPC hardware instead.

Those kind of cartridges are mapped in MSX in $4000 and the first bytes in the cart are:
    ORG $4000
    DEFB "AB"
    DEFW start_cartridge

Now we are going to take a look to this rom in your favourite cpc emulator or your favourite z80 disassembler/debugger or your favourite pcw/msx/zx spectrum emulator for taking a look. This last option is not a joke, those are more advanced for debugging and tracing code, you can put breakpoints in i/o ports or in events as read/write of memory or use the profiler for discovering with address the program is using more and use that for helping in the disassembly task.

The idea is find the access to the i/o ports $98-$99 for the VDP, $A0-$A2 for the PSG and $A8-$AB for the PPI (keyboard mainly) for patch that code. In a begining with a simply jump to a empty function, because we are researching yet.

Lucky for us, it looks there is not INs/OUTs in this game, we could be in one of those games that only use the bios.

Now we are going to discover which bios calls use the game. For example, i add a breakpoint to every bios call in the msx 1 firmware and try to run the game. In the case of winape, i use the next code (source attached to this message):
; ---------------------------------------------------------------------------
; MSX Bios watcher (c) SyX 2014
; ---------------------------------------------------------------------------

; ---------------------------------------------------------------------------
; Set a breakpoint in WinApe
; ---------------------------------------------------------------------------
    DEFB $ED,$FF
; ---------------------------------------------------------------------------
; MSX 1 Bios calls
; ---------------------------------------------------------------------------

; ---------------------------------------------------------------------------
; RST-and other routines
; ---------------------------------------------------------------------------
    ORG  $0000
; CHKRAM (also called STARTUP, RESET or BOOT)
; Function : Tests RAM and sets RAM slot for the system
; Registers: All
; Remark   : After this, a jump must be made to INIT, for further initialisation.

    ORG  $0008
; Function : tests whether the character of [HL] is the specified character
;            if not, it generates SYNTAX ERROR, otherwise it goes to CHRGTR (#0010)
; Input    : set the character to be tested in [HL] and the character to be
;            compared next to RST instruction which calls this routine (inline parameter)
; Output   : HL is increased by one and A receives [HL], When the tested character is
;            numerical, the CY flag is set the end of the statement (00h or 3Ah) causes
;            the Z flag to be set
; Registers: AF, HL   
    ORG  $0038
; Function : Executes the timer interrupt process routine
    JP   interrupt_routine
; ---------------------------------------------------------------------------
    EX   AF,AF'

    CALL H_KEYI ; Call to the interrupt ram hook

    POP  IX
    POP  IY
    POP  AF
    POP  BC
    POP  DE
    POP  HL
    EX   AF,AF'
    POP  AF
    POP  BC
    POP  DE
    POP  HL

; ---------------------------------------------------------------------------
    ORG  $4000
    incbin 'cart.bin'

; ---------------------------------------------------------------------------
    ORG  $FD9A
; Interrupt RAM hook

; ---------------------------------------------------------------------------

The only strange thing is the interrupt routine, the bios interrupt routine is in the rom, because that for programs that need to attach to it, the rom routine makes a call to $FD9A and the program can change the code there to redirect to their own routine or use IM 2.

You can see the details of implementation of this and the rest of msx bios functions in the CBIOS source code, an open source implementation of the MSX bios that is used in emulators. Remember that the msx bios rom is  from each msx manufacturer, sure they are similars between different machines, but the copyright of them are from lovely copyright lovers companies like Sony, Panasonic, ...

Important: the only source generating interrupts in an MSX 1 is the vertical blank signal from the VDP.

Ok, then is time to execute our bios watcher, we set the PC in the emulator to the cartridge start address, $4020 in case of Tank Batallion.
And then we are taking note of every time a breakpoint to the bios is taken, and of course, we can change the breakpoint opcodes to NOP for not being annoyed everytime the same routine is called. We repeat this process , until winape doesn't enter anymore in its debugger.

Ok, now is time to research deeper, we enter in the winape debugger and we can see the code is in a infinite loop in $406E:
$4068 LD   BC,$E201
$406B CALL $0047
$406E JR   $406E
$4070 LD   SP,$FF00
$4073 CALL $013E

Strange! Well, if we take a look to the interrupt ram hook in $FD9A, we'll see that our RET  has been changed to JP $4070. It looks the game interrupt routine is in $4070, just after our infinite loop.
Suspicious! Well, if we look the code just up $4068, we'll see that the game is setting that jump in $FD9A ;)

Because the interrupts are disabled, the game interrupt routine never runs and we get our infinite loop. Ok, no problemo :P, we can reenabled the interrupts in the z80 and see what happen when the interrupt routine is executed.

Well, the interrupt routine is executed and after a while the program return to the infinite loop. Ok, time to go deeper, we are going to search for any jump to $406* in the code, for seeing what is happen. Ok, it's happening that the interrupt routine exits with jumps to $4068.

The code in $4068 is using the bios call WRTVDP ($0047) to write $E2 in the VDP register 1, that means Enable Display, Set Sprite size 16x16 pixels and Enable the VBlank Interrupt.

And that is what happen, the game running fully from the interrupt routine (typical amiga demo code from the end of 80s waiting for the left mouse button :P) , once the task in the interrupt routine is made, the game reenable the Vblank interrupt and wait in a infinite loop until the next vblank interrupt happens.

This is fun!!! (At least for me, but i'm a nerd xDDD) I'm thinking now that i could disable the interrupts and rewrite this msx code like this:
    ORG $4068
    NOP                 ; 00
    LD   A,>PPI_B       ; 3E F5
    IN   A,($00)        ; DB 00
    RRA                 ; 1F
    JR   NC,.wait_vbl   ; 30 F9

We wait to the VBlank and then run the msx game code. Tried (we can corrupt the accumulator, because the "$4073 CALL $013E" is used for read the vdp status in it) and the game looks is working (look to the snapshot attached) and we can see in the CPC screen how the game is using the RAM for variables, stack, ...

Now we need to continue with our research of which bios functions is being used. Until this moment, the functions used are:
WRTVDP ($0047)
Function : write data in the VDP-register
Input    : B  - data to write
           C  - number of the register

WRTVRM ($004D)
Function : Writes data in VRAM
Input    : HL - address write
           A  - value write

FILVRM ($0056)
Function : fill VRAM with value
Input    : A  - data byte
           BC - length of the area to be written
           HL - start address

LDIRVM ($005C)
Function : Block transfer to VRAM from memory
Input    : BC - blocklength
           DE - Start address of VRAM
           HL - Start address of memory

WRTPSG ($0093)
Function : Writes data to PSG-register
Input    : A  - PSG register number
           E  - data write

RDPSG ($0096)
Function : Reads value from PSG-register
Input    : A  - PSG-register read
Output   : A  - value read

RDVDP ($013E)
Function : Reads VDP status register
Output   : A  - Value which was read

SNSMAT ($0141)
Function : Returns the value of the specified line from the keyboard matrix
Input    : A  - for the specified line
Output   : A  - for data (the bit corresponding to the pressed key will be 0)

We can for example, search for CALL $0* in the rest of the cartridge, but it looks there is no extra calls. That means we only need to replace these 8 bios routines (5 related to VDP, 2 to the PSG and 1 for reading the keyboard) for CPC equivalents and the game should work in our machines.

Other idea is looking better how the game code is making these bios calls, because a few ones could be skipped, for example, the game is "uploading" the tiles and sprites images to the VRAM (the RAM in the video chip) and we could skip that and only respect the CALLs that made changes in the tilemap or send sprites coords, ... reducing our vdp emulation to control the tilemap and the sprites or we could make a full vdp emulation, but when graphics are uploaded those are converted from MSX to CPC... depends how good we want to make the conversion, more or less cpc native.

Ok, now we could start to think in the memory map for this project. There is a lot of free RAM in the $0000-$3FFF range, because we only need to put there the code for the 8 bios functions, the game rom will be in $4000-$6000 and the msx ram in $E000-$FFFF. The rest of ram is free for our use, the best place to put the screen is in $8000-$BFFF, we could use the lower RAM for putting there the tilemap and the graphics in CPC format for faster print and there is 8 KBs in $C000-$DFFF, ...

Of course, we should take a look to the game in an MSX emulator for seeing how the game looks and what screen mode is using. The game is using Screen 1, that means the screen is  32x24 tiles, only use a bank of 256 tiles, each tile is 8x8 pixels and each 8 tiles share the same 2 colours. The game is using sprites only for shoots, explosions and certain banners with bonus points.

A nice emulator for MSX 1 games is Meisei (although the site is offline, it's easy to find the last version 1.32 in any emulator place), it's small and let you dump the msx sprites and tiles in the vram directly to png (although is possible to take a look in the winape graphics ripper, in this case in special we can look for mode 2 8x8 graphics to see the tiles and increasing the size we can see the game levels collision maps encoded as 1 bit/tile).

Now is when one of the forum graphicians show amazing CPC retouched graphics based in the MSX version. And for they are those:

Well, this is the moment of coding those 8 "magical" functions that give us a new cpc game, but my plane leaves in a few hours and i need to get ready xDDD

But not only that, at the beginning i spoke about get feedback from the community, this is not a triple AAA game, it's little nice game that we can use for study the viability of another msx ports and i'm sure the problems found making this conversion, can be helpful for another ones, because as i told, japanese games loves to use the bios... Of course, other games can make more heavy use of sprites or tiles, but that is only more fun for us ;)

I'll return after Easter, i don't know if i will be online until, while feel free of discuss this project and taking part in it, coding these functions, making graphics, improving sound, ...

Tchau!!! :)


Now, this is what I call a detailed post!  8)

I think that it would be interesting to port some japanese games over to the CPC as they had many fun games that didn't went out of japan.

And of course, there's the nerdy pleasure on doing it, hehehe!  ;D



Powered by SMFPacks Menu Editor Mod