Chapter 33
A 16 KB ROM cartridge
A cartridge is a program in ROM that the MSX finds and starts on its own, when it is switched on. This chapter builds ROM, the example in A:\TATARA\EXAMPLES\ROM, which makes the image of a 16 KB cartridge: the header the MSX looks for, a routine that prints a line, and the padding that makes the file the right size. It then runs the image in an emulator, and changes it in three ways.
33.1 The example
A cartridge starts before MSX-DOS or MSX-BASIC does: the MSX looks at each slot, finds the cartridge, and calls it while it is still starting. The program can use the BIOS, which is always there, but nothing that MSX-DOS or BASIC provides. ROM sets up a text screen, prints a line, and stays there.
The example has two files, ROM.AS and BUILD.BAT. The result is a file, ROM.ROM, that holds the 16 KB of the cartridge, byte for byte. It is not a program that MSX-DOS can run: it goes into an emulator as a cartridge, or onto a flash cartridge.
33.2 The source
; ROM.AS - a 16 KB cartridge. ; ; A cartridge in page 1 begins at 4000h with a sixteen-byte header. ; The BIOS looks for the letters AB, and calls the INIT address if it ; is not zero - before there is an operating system of any kind. ; ; NOTHING HERE MAY CALL MSX-DOS. There is no MSX-DOS yet. The screen ; and the printing are the BIOS, and the code never returns, because ; there is nothing to return to. ; ; THE LAST TWO LINES ARE WHAT MAKES THE FILE 16 KB. TANREN writes the ; span from the lowest byte of content to the highest, and a gap ; inside that span comes out as zeros. One byte at 7FFFh therefore ; makes the file exactly 16384 bytes, which is what a cartridge has ; to be. A DS would not do it: the space a DS reserves after the last ; byte of content is deliberately not in the file. include bios.inc ; INITXT, CHPUT, and the rest include ascii.inc ; CHR_CR and CHR_LF aseg org 4000h db "AB" ; a cartridge, says the BIOS dw init ; INIT: called at once dw 0 ; STATEMENT: no CALL statements dw 0 ; DEVICE: no device dw 0 ; TEXT: no BASIC program in here dw 0,0,0 ; reserved, and zero init: call INITXT ; a text screen, from nothing ld hl,msg ini.lp: ld a,(hl) or a jr z,ini.end push hl call CHPUT pop hl inc hl jr ini.lp ini.end: jr ini.end ; a cartridge has nowhere to go msg: db "Assembled by Tatara. Linked by TANREN.",CHR_CR,CHR_LF,0 org 7fffh ; the last byte of the 16 KB... db 0 ; ...so that the file is all of it end
33.2.1 The header
A 16 KB cartridge sits at 4000h to 7FFFh, and its first sixteen bytes are a header that tells the MSX what the cartridge holds (figure 33.1):
- ID, two bytes: the letters AB. Without them, the MSX does not take the ROM for a cartridge.
- INIT, the address of a routine the MSX calls when it starts, or 0000h for none. Here it is init.
- STATEMENT, DEVICE and TEXT, the addresses of routines that add CALL statements and devices to BASIC, and of a BASIC program in the ROM. This cartridge has none of them, and they are 0000h.
- Six reserved bytes, which must be 00h.
The MSX looks for the header at 4000h and at 8000h (section 26.6). ASEG and ORG 4000h put it at 4000h, with nothing for TANREN to move.
33.2.2 INIT
init calls INITXT, the BIOS routine that sets up the text screen, prints the message with CHPUT, and then jumps to itself for ever: ini.end: jr ini.end. The MSX stays on the screen the cartridge left it on. This is how a game, for example, takes the machine over.
33.2.3 The last two lines
A cartridge image has to hold all its 16 KB. TANREN writes the memory from the first byte with content to the last (section 24.1.1), so the program alone would make a file of 77 bytes. org 7fffh and db 0 put a byte at the last address, and everything between the message and 7FFFh is written as zeros.
33.3 Building it
rem BUILD.BAT - EXAMPLE: ROM rem rem A 16 KB cartridge image. No MSX-DOS anywhere in it: the code runs rem from a cartridge slot before there is an operating system, so it rem calls the BIOS and never returns. rem rem ASEG and ORG 4000h page 1, where a 16 KB cartridge lives rem no /B a ROM has no header of any kind rem ORG 7FFFH and DB 0 what makes the file exactly 16384 bytes rem rem THIS IS THE ONE EXAMPLE A SUCCESSFUL BUILD DOES NOT PROVE. Load rem ROM.ROM as a cartridge in an emulator, or write it to a flash rem cartridge. It is not a program whose name you can type. rem The headers are in A:\TATARA\INCLUDE and are not copied here. rem TATARA is rule 3 of the include search - see the DIRS example. rem CHANGE THE PATH BELOW if you put the tree somewhere else. set TATARA=a:\tatara\include echo === Assembling tatara /q rom.as rom.tro echo === Linking, with no header of any kind tanren /q /o:rom.rom rom.tro echo === Done. ROM.ROM should be exactly 16384 bytes. set TATARA=
The link has no /B: the file is the cartridge’s own bytes, with no BLOAD header in front of them. Its only header is the one in the source.
A:\TATARA\EXAMPLES\ROM>build === Assembling === Linking, with no header of any kind === Done. ROM.ROM should be exactly 16384 bytes.
Without /Q, TANREN’s summary confirms the size:
A:\TATARA\EXAMPLES\ROM>tanren /o:rom.rom rom.tro ... 1 modules, 6 records, ends at EOF. Wrote ROM.ROM, 4000-7FFF (16384 bytes).
The file starts with 41 42 10 40, AB and the address of init, 4010h, low byte first, followed by the ten zero bytes of the other fields. init begins at 4010h with CD 6C 00, call INITXT.
33.4 Running it
Insert ROM.ROM as a cartridge in an emulator and reset the MSX. In openMSX, for example, the console command carta inserts a ROM image in the first cartridge slot, and reset restarts the machine. The screen shows the message on a text screen, and nothing else:
Assembled by Tatara. Linked by TANREN.
The MSX found AB at 4000h, called init, and never came back from it. Neither MSX-DOS nor BASIC starts.
33.5 Things to try
The files below are not part of the example: make them in its directory, with TATARA set as in BUILD.BAT, and link each one as ROM.AS is linked.
33.5.1 Return from INIT
ROMRET.AS is ROM.AS with ret in place of the endless jump:
ini.end: ret ; and the MSX goes on starting
The message appears for a moment, and then the MSX goes on starting as it would without the cartridge; on an MSX with a disk drive, MSX-DOS starts. The comment at the top of ROM.AS says there is nothing to return to, but the MSX does wait for INIT to return. A cartridge that only adds something to the system, and leaves the rest of the start to it, sets up what it needs and returns.
33.5.2 Spoil the ID
ROMBAD.AS is ROM.AS with "AC" in place of "AB":
db "AC" ; not a cartridge ID
It links into a 16 KB file like the others, but the MSX does not take it for a cartridge. Nothing of it is seen: the MSX starts MSX-DOS as if the slot were empty. When a cartridge does nothing at all, check the first two bytes of the file.
33.5.3 Let TANREN place the cartridge
A cartridge does not have to use ASEG. ROMREL.AS is the same cartridge in the default code segment, placed at 4000h by /P: (section 24.3):
; ROMREL.AS - the cartridge as a relocatable module, placed by /P:4000. include bios.inc include ascii.inc cseg header: db "AB" dw init dw 0,0,0 dw 0,0,0 init: call INITXT ld hl,msg ini.lp: ld a,(hl) or a jr z,ini.end push hl call CHPUT pop hl inc hl jr ini.lp ini.end: jr ini.end msg: db "Placed by TANREN /P:4000.",CHR_CR,CHR_LF,0 ds 3FFFh-($-header) db 0 end
ORG cannot pad a relocatable segment to a fixed address, so the padding is worked out from the size: $-header is the number of bytes so far, and the DS reserves the rest of the 16 KB but one. The DB after it fills the last byte, so that the DS is inside the file and not after its end.
A:\TATARA\EXAMPLES\ROM>tatara /q romrel.as romrel.tro A:\TATARA\EXAMPLES\ROM>tanren /p:4000 /o:romrel.rom romrel.tro ... 1 modules, 7 records, ends at EOF. Wrote ROMREL.ROM, 4000-7FFF (16384 bytes).
As a cartridge, it prints its own line and stays there:
Placed by TANREN /P:4000.
Written this way, the code of a cartridge can be spread over several modules, as any other program, and TANREN places them all.