To assemble your programs you need an assembler for the Intel 8048 processor which is a member of the MCS-48 processor family. For the demo programs in this document I used the freeware assembler ASL. I used the Linux version, but there are also DOS and OS/2 versions available there.
ASL does not generate a ROM file directly, it uses an intermediate file with the extension ".p". There are several tools included in the ASL package that can convert these ".p" files. Use p2bin to generate binary files. It needs to know which address range to extract from the ".p" file. The internal BIOS of the G7000 / Odyssey² occupies the first 1KByte. Nearly all commercial cartridges use only the next 2KByte, so to generate a ROM file for use with o2em the range you need is from 0400h to 0bffh. To mark parameters as hexadecimal p2bin uses the $ sign which needs to be escaped by most shells. So it is more portable to use decimal numbers. To assemble the hello.a48 demo program into a o2em ROM file you need to type:
asl hello.a48 p2bin hello.p hello.rom -r 1024-3071
This example is for Linux, the executable of the DOS version is
called AS.EXE, so you have to substitute asl with as
in the example when using DOS. For the G7000RAM you need to copy the range
1024-4095. Later versions of o2em support 3K per bank, so there is no longer
a difference between o2em and the G7000RAM.
All these examples are for programs with only one G7000 / Odyssey² program bank. For bigger programs I can think of several methods to handle them. The method I currently prefer is to use one assembler file per program bank. It takes takes some time to fill the 2KBytes available with o2em. So if your code reaches that size you will be experienced enough to find the way which is best for your code.
After assembling your programs you have to find a way to run them. You can run the programs on the o2em emulator. If you prefer to test your programs on a real machine you can use the G7000RAM cart. It is a 12K (4×3K) RAM cart with a serial port (RS232) to upload the programs. Find out how to build one on my home page. If you can burn EPROMs you can build an EPROM cart, eg. by hacking an existing cart.
All numbers in hex are written as 0ABCDh in this document. A means the A
register of the 8048, R0-R7 the active register bank, most of the time this is
RB1. When writing R0:5-7 I mean bit 5-7 of register R0. RB0 and RB1 are a
specific register bank, MB0 and MB1 are the program memory banks. P0, P1 and P2
are the external ports. F0 and F1 are flags internal to the 8048. T is the
timer register. 256 bytes together on a 256 byte boundary are one page. All
symbolic labels for the BIOS routines in this document are explained in the summary
chapter and are included into g7000.h.
Symbolic names for VDC registers start with vdc_, those for
internal RAM start with iram_. The names for external RAM start
with eram_. Names starting with col_ are names for
colours.
At power on or after reset the BIOS jumps directly to 0400h, the first
address of the external ROM. Normally you put a
jmp selectgame = 02C3h there, this initialises the
VDC, internal and external RAM. Then it displays the "SELECT GAME" message
and waits for a key. After that it jumps to 0408h. There you have to start
your code with another jmp, because 040Ah is used by the
interrupt. Which key was pressed is stored in A. All sprites, chars, quads and
the grids are initialised, the VDC is enabled and the graphics are on.
If an external interrupt occurs, the BIOS jumps to 0402h. From there you can
insert some code into the interrupt. After that do a jmp 0009h, I
call this irq. The BIOS checks for VSYNC (Vertical sync, marks
the beginning of a new frame) and jumps to 0406h, if VSYNC. Here you can
insert again some code. Continue to vsyncirq = 001Ah.
After processing the register transfer table the BIOS jumps to 040Ah, if there
is a sound event to process. Again you can insert some code here. The sound
event gets processed at soundirq = 0044h. So if you
don't want to insert code anywhere into the interrupt the beginning of your
program looks like:
include "g7000.h"
org 0400h
jmp selectgame
jmp irq
jmp timer
jmp vsyncirq
jmp start
jmp soundirq
timer retr ; timer interrupts are discussed later
start ; your code starts here
In this section I will explain my hello.a48
program. It is very important not to change the VDC registers while displaying
graphics, at least not if you want predictable results. So there are two
routines to turn the graphics off (gfxoff = 11Ch) and on
(gfxon = 0127h). To display characters there is
printchar = 03EAh. You put a pointer to the character
to display into R0, for example vdc_char0 = 010h as the
first character. In R3/R4 you put the X/Y position on the screen. R6 is the
colour of the character, for example
col_char_white = 00Eh. In R5 you tell which character
to display. After calling printchar, R0 is set to the next character and R3 is
advanced right for 8 pixels, so you can print several characters into one line.
The following code prints the traditional "HELLO WORLD" onto the
screen.
start
call gfxoff ; switch the graphics off
mov r0,#vdc_char0 ; start char
mov r3,#20h ; x-position
mov r4,#20h ; y-position
mov r2,#0Bh ; length
mov r1,#hellostr & 0FFh ; the string to print
; must be in the same page
loop mov a,r1 ; get pointer
movp a,@a ; get char
mov r5,a ; into to right register
inc r1 ; advance pointer
mov r6,#col_chr_white ; colour
call printchar ; print it
djnz r2,loop ; do it again
call gfxon ; lets see what is written
stop jmp stop ; Thats all
hellostr db 1Dh, 12h, 0Eh, 0Eh, 17h, 0Ch
db 11h, 17h, 13h, 0Eh, 1Ah