Table of Contents

- Table of Contents

- Introduction

- Tutorial

- Quick Start and Reference

- IO Programming (1)

- IO Programming (2)

- Modules

- IO Reference

Introduction

JBit

This is the main documentation for JBit. Select the "About" item on the menu for the list of the key bindings.

JBit is a microcomputer in a phone, and with it you can learn and practice programming.

JBit is free, and does not require a data connection. However, it does come without any warranty and it requires dedication and time to be used. Even large programs like mini games can be done, but this requires some experience, the ability to plan ahead, and most likely the use of pencil and paper to organize the code.

If you don't have JBit already, I advise you to download it. Here is the URL of the main website together with its shortened version.

jbit.sourceforge.net
jbit.sf.net

It is fairly lightweight and it should be accessible by most phones. If you have problems with it or want to save even more kilobytes of bandwith, a limited wap site is also available:

jbit.sf.net/m

News

JBit 2.1.3

Device std replaces stdout. Completed FONT tileset. io2sim: Minimal GAMESET implementation.

JBit 2.1.2

io2sim: Images and IPNGGEN. MS-DOS minimal libretro frontend.

JBit 2.1.1

io2sim: Palette and console. Added device id in .jb header.

JBit 2.1.0

io2sim: libretro core and win32 IO2 simulator (MicroIO only for now). ZOOM2 flag.

JBit 2.0.3

The new GAMESET request replaces the old template.jb / JBitGameKit.

JBit 2.0.2

Basic TTY support in xv65. MicroIO ported to MS-DOS.

JBit 2.0.1

Record Store API. Android binaries (for terminals, no app). Removed js version.

JBit 2.0.0

Merged the J2ME, native and javascript repositories.

JBDoc-20130829 / 1.1.0

There is a new book: ioprg1.

Versions

JBit

If you have a fairly recent phone, this version should work and it is the recommended one.

JBit2M

If you have a recent (MIDP2) phone, but JBit is too big to run (or to download), this is another option.

JBit1M

This is for old (MIDP1) phones. You cannot write interactive, graphical games or generate images with it, and this version does not include the demos. However, it might be the only version that works with your phone. In the past, I have tested it on a real Nokia 3410 and it worked.

Documents

Select the "Books" item on the menu for the available documentation. JBDoc is a work in progress and the following list is subject to change.

tutorial

This is a tutorial that has been written for (and used successfully by) people with no previous experience in programming. Once you understand it, you should be able to study the 6502 demos.

qsref

If you have some previous experience with assembly programming, this is a terse quick start, followed by some support tables. It should contain everything you need to know to get you started writing simple, small programs with JBit.

ioprg*

A guide on how to program the IO chip. There is an introduction to the GAMESET, IO requests, a fairly complete treatment of images and an introduction to sprites.

modules

At the moment, just a short description of the Paint module.

ioref

A short description of the registers and requests of the IO chip. It is terse and it might not make sense to you at all. You can use it to try to decypher the IO1/IO2 demos.

Beginner's Tutorial

Introduction

JBit is a complete programming environment and is more complex than a typical mobile phone application. This tutorial will help you to get started.

The first thing to understand is that, when you are writing a program using JBit, you are not writing a program for your mobile phone, but for the JBit Virtual Machine (MicroIO Edition), or VM.

Here there is a picture of the VM:

As you can see, IT looks like an ugly mobile phone. Now, the VM might be ugly, but it is definitely not a mobile phone. For a start, you cannot make or receive calls, nor send or receive text messages with it. Furthermore, the display can only show a matrix of 10x4 characters and the keypad has only the standard keys (i.e. 0-9, * and #).

Since the VM cannot function as a phone, what can it be useful for? Well, the VM is pretty much useless. However, learning how to program it can help you to understand how computers work. Computers are complex devices and their inner core is hidden by several layers of abstraction, but they do have a core and their core is not much more complex than the VM.

Another thing to understand is the difference between the VM and JBit. JBit includes the VM, but it is not only that. It also includes tools to help you to edit and manage programs for the VM.

Actually, you cannot reach the VM until you make a program for it. The VM itself is empty and has no such a thing such a Desktop that you can play with. Therefore, our first task will be to create an empty program, so that we can have a look at the VM.

Here is how JBit looks like when you start it (you might have more options in your version of JBit; just ignore them for now):

Select Editor from the list.

The Editor needs to know how big the program is. At this stage we do not really care and so we can just confirm 4 Code Pages and 3 Data Pages and select OK.

On some phones, OK is directly mapped to a soft key, while on other phones, OK is available using a menu (e.g. Options or Menu). If you do not see the Size (New) screen, it probably means that you already have one program loaded and should restart JBit.

You should now see a matrix of zeros:

This is the program. We will come back to it later. For now, we only want to quickly get to the VM.

Select Debug.

Inside the VM

Here is the VM:

It does not look anything like the picture above. This is because you are actually looking inside the VM!

Press # to switch to the outside view.

Now you are looking at the display of the VM. There is no point in showing you the keypad of the VM, since the keypad of your phone can act as the keypad of the VM.

There is a minor complication here. If we started the VM properly, the VM would have halted immediately, because the program is empty. Instead, we started the VM in a frozen mode that allows us to inspect it. In this mode of operation, it is far more common to look at the display than to use the keypad, so, right now, the keypad of your phone is not acting as the keypad of the VM. Instead, the keypad of your phone is used to switch back to the inside view.

Press # again to switch back to the inside view.

Let us have a look at what is inside the VM.

You have probably already heard of the term CPU (Central Process Unit), as it is one of the criteria of selection when shopping for a computer. The CPU used by the VM is essentially a 6502, a CPU popular during the 70s and the 80s. It is smaller and slower, but, from a theoretical point of view, no less powerful than a modern CPU.

Gaining an understanding of the CPU is the whole point of JBit and will take a while. We can start with this: the CPU is an agent acting on a virtual world on our behalf. Before letting the CPU act on our behalf, we will play a bit with this virtual world ourselves.

Memory

Press 0 to switch to the MEMory view.

Unlike in the real world, where we are free to place objects as we like, in this virtual world, objects are placed in cells and cells are disposed in a single row.

Also, unlike in the real world, where there is a variety of objects, in this virtual world, there is only one kind of object: the byte, a small integer ranging from 0 to 255.

Finally, unlike the real world, this virtual world is finite. There are exactly 65536 cells. To better manage this long sequence of cells , we partition it in 256 segments (called pages) of 256 cells each. This has the nice side effect of allowing us to refer to the location of a cell using two bytes: the number of the page (using 0 for the first page and 255 for the last) and the position (or offset) within that page (using 0 for the first position and 255 for the last). This pair of bytes is called the address of a cell and is written as two numbers separated by a colon. For example, the first cell is at address 0:0, the last one is at address 255:255 and the 260th one is at address 1:3 (i.e. the 4th cell of the 2nd page). We will often just write the cell Page:Offset to refer to the cell at address Page:Offset.

The MEMory view is a view on this virtual world. To make the most of the small display of a typical mobile phone, cells are shown on a matrix, but you should always keep in mind that they are in fact disposed in a single row.

The current cell (i.e. the cell we are currently inspecting) is marked by the cursor. The cursor can be moved by using 4 (or left) to go to the previous cell and 6 (or right) to go to the next one. You can also use 2 (or up) and 8 (or down).

Reach the cell 3:25 and then select Edit.

This screen allows you to change the content of a cell.

Input 56 into the Value field and then select OK.

The content of the cell has changed. Move the cursor further down until the cell 3:25 is not visible anymore (e.g. until you reach 3:50) and then back until the cell 3:25 is visible again. You can see that the cell has kept the value you have put into it. The cell 3:25, like most of the cells, is a memory cell, that is, a cell that just keeps the value that is put into it.

IO chip

Select GoTo.

This screen allows you to quickly move the cursor to a specific cell.

Input 2 into the Page field, input 40 into the Offset field and then select OK.

Cells on page 2 are not memory cells. Instead, they are links to a component called the IO chip. In particular, cells from 2:40 to 2:79 are connected to the display, in this way:

0 1 2 3 4 5 6 7 8 9
40 . . . . . . . . . .
50 . . . . . . . . . .
60 . . . . . . . . . .
70 . . . . . . . . . .

Putting a byte from 32 to 126 into one of the cells above will cause a character to appear on the display, according to this table:

0 1 2 3 4 5 6 7 8 9
30       ! " # $ % & '
40 ( ) * + , - . / 0 1
50 2 3 4 5 6 7 8 9 : ;
60 < = > ? @ A B C D E
70 F G H I J K L M N O
80 P Q R S T U V W X Y
90 Z [ \ ] ^ _ ` a b c
100 d e f g h i j k l m
110 n o p q r s t u v w
120 x y z { | } ~

Select Edit, input 88 into the Value field and then select OK.

Press # to switch to the outside view.

You can see that the character X is visible on the top left corner of the display. On phones with large displays, the character will likely be more toward the center, since the display of the VM is far smaller than the display of the phone.

Now that we have a better understanding of what "acting on a virtual world" means, we are ready to take another look at the CPU.

CPU

Press # again to switch back to the inside view and then 0 to switch to the CPU view.

The CPU can perform a limited number of elementary actions, called instructions or operations. For example: reading a byte from a cell, writing a byte into a cell, adding two bytes together, etc...

A program is essentially a script detailing the sequence of operations that the CPU must perform to produce the desired behaviour.

The CPU retrieves the operations to perform from the cells, starting from the cell 3:0. What for us is just a number like any other, for the CPU has a specific meaning. For example, 0 means BRK (abbreviation of BReaK) for the CPU. The name BRK comes from the fact that, on a real 6502, BRK could be used to suspend the program. This is one of the few differences between a real 6502 and the CPU of the VM. On the CPU of the VM, BRK causes the program to terminate.

Places in the CPU are called registers instead of cells. We will examine only a few of them now. First, there is the PC (or Program Counter). It has room for two bytes and contains the address of the cell containing the next operation to perform. Then there are three registers that have room for a single byte each. They are: the Accumulator (abbreviated by A), X and Y.

Press 0 to switch to the MEMory view.

Write 232 into the cell 3:0. That is: select GoTo, input 3 into the Page field, input 0 into the Offset field, select OK, select Edit, input 232 into the Value field and then select OK.

Press 0 to switch back to the CPU view.

232 means INX (abbreviation of INcrement X) for the CPU. INX causes the X register to be incremented by 1. If the X register already contains 255, it is reset to 0.

Press 1 to advance one step (i.e. let the CPU perform one operation).

You can see that the operation has been carried out: the register X now contains 1 and the PC now points to the next cell (i.e. the cell 3:1, containing the next operation to perform).

We could change the content of the cell 3:1 and repeat the process again, but this is not the best way to do it. Writing sequences of instructions is better done using the Editor.

Press 1 to advance one step and terminate the program.

You are given the last chance to have a look at the display, before powering off the VM and returning to the Editor.

Select End.

Editing programs

The Editor is a modal editor, i.e. the effects of pressing a key depend on the mode of operation of the editor. There are two major modes of operation (NAVigation and EDiTing) combined with two minor modes of operation (MEMory and ASseMbly).

The NAV MEM mode behaves like the MEMory view described before. Just like before, you can move the cursor using 4 (or left), 6 (or right), 2 (or up) and 8 (or down). The main difference is that you cannot reach the cells beyond the limit of the program. This is because, when writing a program, you are not editing the memory of the VM, but merely writing a template of bytes that will be later used to initialize it.

Make sure that the cursor is at 3:0 and press 5 (or fire, on some phones).

The EDT MEM mode allows typing the bytes of the program "in place" (i.e. without opening a new screen).

Press 2, *, 2, 3, 2 and 2.

Note the following:

1. You are not limited to typing one byte, but you can type a whole sequence of bytes.

2. For bytes with fewer than three digits, you can move to the next byte by pressing *.

3. For bytes with exactly three digits, moving to the next byte is automatic.

If you make a mistake typing the sequence, you can select Cancel to clear the current byte. If the current byte is already cleared, selecting Cancel will move to the previous byte. Finally, if you reach the first byte and select Cancel, you will return to the NAV MEM mode without making any changes to the program.

Press OK to confirm the changes and return to the NAV MEM mode.

The first three bytes of the program should now be: 2, 232 and 2. You might recognize 232 as meaning INX, but what about 2?

Press #.

The NAV ASM mode allows you see the program as it would be interpreted by the CPU. You can move the cursor using 2 (or up) and 8 (or down) to scroll the listing, but you cannot press 5 to edit it in place.

So, 2 means ??? for the CPU. Well, that was not very helpful. Let us start the VM (properly this time) to see what happens.

Press *.

Here is what happened:

1. The VM was powered on.

2. The program was copied to the memory cells of the VM.

3. The CPU read the cell 3:0 to get an operation to perform.

4. Since 2 has no meaning for the CPU, the VM was halted with an error.

INV.OP. is the abbreviation of "invalid opcode". Opcode is the contraction of "operation code" and is a byte identifying what kind of operation to perform. Some opcodes (e.g. 232) make sense for the CPU, while some others (e.g. 2) do not.

Select End to return to the Editor and then press # to switch to the NAV MEM mode.

Opcodes and Addr. Modes

Before going any further, let me rephrase the sentence:

232 means INX for the CPU.

Without using the misleading word "means" ("means" might suggest that the CPU understands something):

When the CPU reads the opcode 232 from a cell, it increments the X register.

The three-letter label INX is called mnemonic and its only purpose is to remind the programmer of what the CPU does.

Let us have a look at two new opcodes: 169 and 173.

169. When the CPU reads the opcode 169 from a cell, it replaces the content of the Accumulator with the content of the cell after the cell containing the opcode.

173. When the CPU reads the opcode 173 from a cell, it replaces the content of the Accumulator with the content of a specific cell. The address of that cell is computed using, as the offset, the content of the cell after the cell containing the opcode, and, as the page, the content of the cell after the cell providing the offset

The wording is a bit convoluted, but I hope it will become clear with the examples below. What is important right now is to see a similarity between the two opcodes: in both cases, the content of the Accumulator is replaced. The mnemonic for that is LDA (abbreviation of LoaD Accumulator).

Here is an example for the opcode 169:

169 65

Note that, unlike BRK and INX, one byte is not enough to specify the behaviour of the CPU. If we want the CPU to replace the content of the Accumulator, we must specify the new content (65 in this case). In other words: the operation beginning with the opcode 169 is two-bytes long and the second byte (a.k.a. the operand) specifies the new content of the Accumulator.

And here is an example for the opcode 173:

173 40 2

In this case the operation is three-bytes long, and the last two bytes (i.e. the operand) specify the address of a cell where the new value of the Accumulator can be found. In this case, that would be cell 2:40. Yes, the order is inverted and this might be confusing at first, but you will quickly get used to it.

If you look at the two sequences of bytes above, you see that it is not clear that they are doing something similar (i.e. replacing the content of the Accumulator). Furthermore, on a long program composed of several operations, it is difficult to spot where the operations begin and to check if you provided the right number of bytes as operands.

Consider this sequence:

169 40 2

Maybe you meant to load the Accumulator from the cell 2:40. But, of course, the CPU cannot guess what you meant; it would load the Accumulator with 40 and then halt the VM because 2 is not a valid opcode.

Assembly

The assembly language is an effective method to present the bytes of a program. Every operation is clearly shown in its own line, no matter if it is 1, 2 or 3 bytes long. Its behaviour is unambiguously specified using its mnemonic and, if needed, its operand. The operand itself is formatted using a rigorous pattern. This pattern is a compact way to express the second part of the two sentences above, or, in 6502 jargon, the addressing mode.

Memory view   Assembly view   Explanation
-----------   -------------   ----------------------------------
169 65        LDA #65         LoaD the Accumulator [LDA] with
the constant [#n] 65
173 40 2      LDA 2:40        LoaD the Accumulator [LDA] with
the content of the cell [n:n] 2:40

The addressing modes here are #n and n:n, where n is the abbreviation of number.

The "qsref" page contains the full list of the addressing modes of the CPU. You are not expected to understand all of them by the brief descriptions contained in that page, but, even if you do not understand them, you can still get some information from the way they look: the number of lower-case characters of an addressing mode is the number of extra bytes an operation needs. For example, the addressing mode n:n,X requires two extra bytes beside the opcode.

Assembly, like every programming language and unlike natural languages, is a formal language and every small detail matters. For example, in English, you might make a punctuation mistake, but you can still be understood. In Assembly, even punctuation is critical. The operations LDA #65 (i.e. 169 65) and LDA 65 (i.e. 165 65) are completely different (the second one, if we ignore the fact that is shorter and faster, is equivalent to LDA 0:65).

Your First Program

Place the cursor at 3:0, press 5 to switch to the EDT MEM mode and then press # to switch to the EDT ASM mode.

The EDT ASM mode allows you to lookup a valid opcode.

Press 5 (jkL), 3 (Def) and 2 (Abc).

Select LDA #n from the list. 169 is inserted, the cursor is moved to the next cell and the mode is switched back to EDT MEM.

Press 6, 5 and * to insert the operand.

Press # to switch to the EDT ASM mode.

Press 7 (pqrS), 8 (Tuv) and 2 (Abc).

STA is the mnemonic of STore Accumulator and is in a way the opposite of LDA: it replaces the content of a cell with the content of the Accumulator.

The "qsref" page contains the full list of the mnemonics of the CPU. Again, you are not expected to understand all of them by the brief descriptions contained in that page, but, even without looking at the list, you should already be able to guess what a few other valid mnemonics (LDX, STX, LDY, STY, INY, DEX and DEY) stand for.

Select STA n:n from the list. 141 is inserted, the cursor is moved to the next cell and the mode is switched back to EDT MEM.

Press 4, 0 and * to insert the first byte of the operand (i.e. the offset).

Press 2 and select OK to insert the second byte of the operand (i.e. the page).

Press # to switch to the NAV ASM mode.

Here is the Assembly view of the complete program:

LDA #65
STA 2:40
BRK

To sum up:

1. The first operation loads the Accumulator with the value 65.

2. The second operation stores the Accumulator (i.e. 65) to the cell 2:40, causing the character A (see the table above) to appear at the top left corner of the display.

3. The third operation halts the VM.

Select Debug to test the program.

Press 1 to advance one step. The Accumulator now contains 65 and the Program Counter 3:2.

Press 1 to advance another step. The Program Counter now contains 3:5. You can check that the character A is visible on the display of the VM by pressing # and then go back to the CPU view by pressing # again.

Select Abort to terminate the program and go back to the Editor.

At the beginning, you are likely to write very short programs and saving your work should not be a priority. In fact, starting from scratch each time might help you to consolidate what you have learned. If in the future you begin writing larger programs, here is how you can save them:

Select Save, type Tutorial and select OK.

Select Back followed by Exit to terminate JBit, and then start JBit again.

Modules and Versions

JBit is a modular system and can be configured to include more or fewer tools, depending on the limitations of your phone. The main menu lists which tools have been included.

In this tutorial I am using JBit1M, a version of JBit targeting old phones that comes with only two tools: the Store and the Editor. We have already used the Editor and below we are going to use the Store, but before that, let me introduce a couple of tools that you probably have in your version of JBit.

Demos

This tool provides a few ready-made programs that you can study. In particular, the 6502 demos are especially designed for the beginning programmer. If you have problem installing a version of JBit including the Demos tool, you can type the 6502 demos manually using the JBit-E0.pdf sheet as a reference.

Paint

This tool allows you to create and edit simple images with JBit.

An important thing to understand is that the tools operate on the current program. Unlike most computer applications, JBit can only work on one "document" at a time; if you run a demo or load a program, the program you are working on will be silently replaced, even if it has not been saved.

You might wonder why there is a Paint tool, if the display of the VM cannot show images. The reason is that you can equip the VM with different versions of the IO chip:

MicroIO

The MicroIO version provides only the most basic facilities to write interactive programs.

MIDP1

The MIDP1 version adds various functionalities, but, most importantly, adds the ability to display images.

MIDP2

The MIDP2 version adds the ability to write 2D action games.

The MicroIO version should not be dismissed. You can learn it quickly and it so simple that you do not need to consult a reference to use it. Even if you have a better version available, targeting the MicroIO version when you write your programs is a very good idea.

Store

Select Store.

The Store tool gives you a container where you can keep your programs for future editing. You have already used the Store tool without realizing it, when you saved the program from within the Editor, but accessing it manually gives you more options. The list of the saved programs is presented to you and you can use a few commands to manage them.

Load&Edit (the default command) loads a program and starts the Editor. Load&Run loads a program and starts the VM. Load just loads a program.

Save asks for the name of the current program and saves it. You cannot overwrite an existing program using Save; for that you have to use Overwrite.

Info, Copy, Rename and Delete do pretty much what you expect them to do. They do not change the current program.

When you upgrade JBit to a new version, your phone should ask you if you want to keep its data and it is important to reply appropriatelly to keep your saved programs. The exact wording of the question varies from phone to phone.

Select Tutorial.

Editing session

Here is a quick editing session, as a review:

1. Press 5 to start editing.

2. Press # to lookup the first opcode.

3. Type 539 (for LDX).

4. Select LDX #n from the list.

5. Type 88* to enter the operator.

6. Press # to lookup the second opcode.

7. Type 789 (for STX).

8. Select STX n:n from the list.

9. Select OK to confirm the changes (the result should be: 162 88 142 40 2).

10. Press * to run the program.

11. Select Video to have a look at the display.

12. Press any key (e.g. the soft key you have used for selecting Video, even if it does not have a label attached to it anymore) to go back to the HALTED screen.

13. Select End to go back to the Editor.

14. Select Save to save the new version of the program to the Store.

Select Back and then Exit to terminate JBit.

Quick Start and Reference

This document is derived from the original sheet found here:

http://jbit.sourceforge.net/doc/JBit-QS.pdf

It is quite terse, as it was written to be printed on a single double sheet of paper and used as a reference. It assumes some experience with Assembly programming.

Getting Started

If you are new to 6502 programming, read the online tutorial and then run some programs step by step. From the JBit menu, select Demos and then 6502. Study all the programs (they are very short) in the order they are presented. First select the option Info to get some hints about the program, then select the option Load&Debug to start the Monitor.

Monitor Status Bar: At the right there is the indication of the current view (CPU or MEM). In CPU view, at the left there is the PC of the next instruction. In MEM view, at the left there is the current cell address.

Monitor Keys: 2, 4, 6 and 8 are the cursor keys. 1 performs 1 step. 3 performs n steps (you can select n by using the option StepSize; default is 10). 7 goes to the next line. 9 steps out of the current subroutine. 0 switches between CPU and MEM view. * continues the program (you can then pause it by pressing a soft key). # shows the video (you can then go back to the monitor by pressing any key). In MEM view, 5 changes the value of a cell. The option Edit can be used to change the registers of the CPU.

Editor Status Bar: At the right there is the indication of the current major (NAV and EDT) and minor modes. At the left there is the letter C if you are on a code page or D if you are on a data page followed by the current cell address.

Editor NAV Mode Keys: 2, 4, 6 and 8 are the cursor keys. 5 switches to EDT mode. 1 goes to the previous snap point, 3 to the next one. 7 sets the mark and 0 swaps the cursor and the mark. 9 goes to the address of the operand. * runs the program. # switches between ASM and MEM minor modes.

Editor EDT Mode Keys: * moves to the next cell. # switches between ASM and MEM minor modes. In MEM minor mode, 0-9 are used to enter the decimal value of the cell. In ASM minor mode, 2-9 are used to enter the letters of the mnemonic.

Editor Example: To enter LDA #12 press 5 to enter EDT mode, then press # to enter ASM minor mode, then press the sequence 5 JKL, 3 DEF and 2 ABC for L-D-A, then select the instruction LDA #n from the list, then press 1 and 2 to enter the operand and finally select OK to confirm the instruction.

System Overview

Neither interrupts nor BCD mode are supported. SED, RTI or any invalid opcode cause the program to abort. To end the program use the BRK (0) opcode.

By convention a program is a series of code pages containing a single stream of valid instructions followed by a series of data pages. Programs structured in this way are said to be well-formed. JBit neither needs nor enforces this convention, but some editing operations (e.g. Resize) are only available on well-formed programs.

The hexadecimal notation is cumbersome on mobile phones; it is therefore common in JBit to use the decimal notation. Absolute addresses are presented in the non-standard page:offset notation.

The program is loaded and starts at the beginning of page 3. Page 255 is reserved for future use. The IO chip is mapped to page 2.

IO Basic Operations

IO registers are stated as decimal offsets relative to page 2.

The console video memory is a 10x4 matrix of extended Latin1 characters starting from CONVIDEO and disposed in row-major order.

CONVIDEO 40 (40 bytes)

The frame refresh is controlled by two registers:

FRMFPS   17
FRMDRAW  18

FRMFPS is the number of frames per second multiplied by 4 (e.g. 40, the initial value, is 10 fps). Writing into FRMDRAW causes the CPU to be suspended until the current frame has been drawn.

Random numbers <= n are read from RANDOM. Writing 0 to RANDOM swaps the current sequence generator: time-based (default) or deterministic. Any other value sets n (default is 255).

RANDOM 23

The standard KeyPresses (the ones that can be represented by ASCII codes; usually only 0-9, # and *) are enqueued starting from KEYBUF; the rest of the buffer is filled with 0s. Write into KEYBUF to consume a KeyPress. If the buffer is full when a new key is pressed, that KeyPress is lost.

KEYBUF 24 (8 bytes)

Charset

Latin1

0 1 2 3 4 5 6 7 8 9
30       ! " # $ % & '
40 ( ) * + , - . / 0 1
50 2 3 4 5 6 7 8 9 : ;
60 < = > ? @ A B C D E
70 F G H I J K L M N O
80 P Q R S T U V W X Y
90 Z [ \ ] ^ _ ` a b c
100 d e f g h i j k l m
110 n o p q r s t u v w
120 x y z { | } ~

(chars 161-255 omitted)

Line Art

T      +-+-+-+-+-+-+-+-+
T      |1|0|0|0|B|R|L|T|
LLLXRRR   +-+-+-+-+-+-+-+-+
B       1 6 3 1 8 4 2 1
B       2 4 2 6
8

(chars 128-143 omitted)

Conversions

0  0000  0    0
1  0001  1   16
2  0010  2   32
3  0011  3   48
4  0100  4   64
5  0101  5   80
6  0110  6   96
7  0111  7  112
8  1000  8  128
9  1001  9  144
10  1010  A  160
11  1011  B  176
12  1100  C  192
13  1101  D  208
14  1110  E  224
15  1111  F  240

6502 Operations

BRK: BReaK; in JBit, used to halt the VM.

NOP: No OPeration.

LDA, LDX, LDY: LoaD Accumulator/X/Y.

STA, STX, STY: STore Accumulator/X/Y.

INX, INY, INC: INCrement X/Y/memory.

DEX, DEY, DEC: DECrement X/Y/memory.

TAX, TAY, TXA, TYA, TSX, TXS: Transfer register.

CMP, CPX, CPY: CoMPare with accumulator/X/Y.

JMP: JuMP.

JSR: Jump to SubRoutine.

RTS: ReTurn from Subroutine.

CLC, CLV: CLear Carry/oVerflow.

SEC: SEt Carry.

BEQ, BNE: Branch if EQual/Not Equal (z flag).

BMI, BPL: Branch if MInus/PLus (n flag).

BCC, BCS: Branch if Carry Clear/Set.

BVC, BVS: Branch if oVerflow Clear/Set.

ADC: ADd (to/into accumulator) using Carry.

SBC: SuBtract (from/into accumulator) using Carry.

AND: bitwise AND (with accumulator).

ORA: bitwise inclusive OR (with Accumulator).

EOR: bitwise Exclusive OR (with accumulator).

BIT: test BITs: #6 to v flag and #7 to n flag.

ASL, LSR: Arithmetic/Logical Shift Left/Right.

ROL, ROR: ROtate Left/Right.

PHA, PLA: PusH/PuLl Accumulator.

PHP, PLP: PusH/PuLl Processor status.

6502 Operands

#n: Constant n.

n: Cell 0:n.

n:n: Cell n:n.

n:n,X: Cell X cells after n:n.

n:n,Y: Cell Y cells after n:n.

r: Next cell + relative offset r, shown as n:n.

n,X: Cell 0:(n+X modulo 256).

n,Y: Cell 0:(n+Y modulo 256).

(n:n): Cell pointed by n:n.

(n,X): Cell pointed by 0:(n+X modulo 256).

(n),Y: Cell Y cells after cell pointed by 0:n.

6502 Examples

TAX: Transfer (i.e. copy) Accumulator into X.

LDA #2: LoaD Accumulator with constant 2.

LDX 2: LoaD X with content of cell 0:2.

STX 2:18: STore (i.e copy) X into cell 2:18.

BCC 6: If C=0, skip (i.e. jump forward) 6 bytes.

BCC 240: If C=0, jump back 16 (i.e. 256-240) bytes.

IO Programming (1)

In this tutorial I assume that you have read the Beginner's Tutorial, spent a bit of time studying the 6502 examples and written some snippets of code targeting the MicroIO chip.

Tiles

The MIDP2 IO chip (called simply IO2 from now on) provides essentially the same level of control that professional game programmers have when writing 2D action games. This flexibility comes at a price: complexity.

Most of this complexity is usually at the initial stage of the program. How many objects are there? What is their shape and color? What about the playfield? How big is it? Does it scroll horizontally? Vertically? Not at all? All of these questions must be answered, answered of course using bytes... You can imagine that it is not going to be easy.

A short sequence of register settings, GAMESET, provides a set of answers:

LDA #60
STA 2:1
STA 2:2

The answers that GAMESET provides are designed to setup a playfield that will feel like the MicroIO display from the inside, but that will look prettier from the outside. Instead of displaying letters, symbols and numbers, you can display painted tiles chosen from a set of 56 tiles:

+---------+
|         |
|   56    |
|  tiles  |
| omitted |
|         |
+---------+

Thanks to:

http://www.famfamfam.com/lab/icons/silk/

Here is the description of the first 7 tiles:

1 shape_square
2 lightbulb_off
3 lightbulb
4 lock_open
5 lock
6 emoticon_unhappy
7 emoticon_smile

So, is it just a matter of writing 3 into 2:42 to make a bulb appear? Unfortunately not. A direct link of cells to tiles is fine for a small grid, but with IO2 you can define grids of thousands of tiles. Nothing prevents you from defining a grid of, say, 500x500 tiles (i.e. 25000 tiles) and show only a portion of them.

Having said that, GAMESET actually configures as many tiles as possible to fit on the display of your phone. For example, on a phone with a resolution of 176x220 pixels, 11 columns of 13 rows of tiles are available (each tile as configured by GAMESET is 16x16 pixels big).

So, how can you access a potentially large number of tiles with the IO chip? One at a time. The IO chip has the notion of the Current Tile. You can use the following cells:

Reg    Content
----   -----------------------------------
2:87   Column of the Current Tile
(initially 0, the leftmost column).
2:89   Row of the Current Tile
(initially 0, the topmost row).
2:91   The tile itself
(initially all 0, empty cells).

After all this theory, let's do some practice. Create a new program and, starting from 3:0, type (GAMESET):

LDA #60
STA 2:1
STA 2:2

Then (from 3:8):

LDA #2
STA 2:87
LDX #3
STX 2:91
INC 2:89
STX 2:91
INC 2:89
STA 2:91

Animation (1)

Animation is essentially about timing and loops.

Programs so far have been "linear", i.e. after a few instructions, the program would reach a BRK and terminate. To be able to write animations, we need to be able to repeat a sequence of instructions over and over again. The easiest way to do it is to use JMP, for JuMP. It allows us to change the PC of the CPU, thus changing the address of the next instruction to execute.

With this knowledge, you might think of writing the following program (starting from 3:8, I assume that you have typed the GAMESET sequence already):

INC 2:91
DEC 2:91
JMP 3:8

Unfortunatelly, this program does not work. Well, it might work, but it is so unpredictable and inefficient that you really do not want to run it.

It is unpredictable, because JBit tries to run the program as fast as it can, and the actual speed varies a lot from phone to phone. My tests and the feedback that I have received suggest that JBit is able to simulate from around 10,000 instructions per second on slow phones to around 500.000 instructions per second on fast ones.

It is inefficient, because it would try to change the tile too often. Even on a slow phone, the tile would change thousands of times per second.

Actually, all this work would be wasted, because IO2 would not even bother to keep up. IO2 would update the tile eventually, but you have no idea how often. It might even be the case that IO2 would use the same value more than once. What you would see is effectively a sampling of a very fast variable. The odds are not even 50%, as the value 0 is more likely.

There is a better way. When you are finished arranging the tiles, you can tell IO2 that now is the time to update the display. You do so by writing any value into 2:18. This has the nice side effect that JBit will suspend its CPU for a while and tell your phone that JBit is idle. Some phones might not care, but some might be able to switch their CPU into low-power mode in turn, draining less power from the battery.

So, this is a predictable and efficient version of the program above:

INC 2:91
STA 2:18
DEC 2:91
STA 2:18
JMP 3:8

Animation (2)

Usually, we don't want to repeat a sequence of instructions forever, but just for a little while. We can do this by using BNE, for Branch if Not Equal. Branch instructions change the PC only if a specific condition is met. In the case of BNE, Not Equal is a bit misleading, as the condition really is Not Zero and it refers to the result of the instruction before the branch.

For example, the following sequence:

...
LDA #3
BNE 3:20
...

Will continue to 3:20, while the following:

...
LDA #0
BNE 3:20
...

Will continue to the next instruction after BNE.

There is a slight complication here. If you try to type in BNE, you will notice that the format is BNE r, not BNE n:n as you might expect.

The reason is that branches usually point to an instruction nearby and two bytes would often be wasted. The single byte operand is used as the number of bytes to skip forward (if between 0 and 127) or backward (if between 128 and 255, where 255 means 1, 254 means 2, etc...). Fortunately, there is no need for you to count them.

We will use the following code as an example:

LDX #30
STA 2:18    [1]
DEX
BNE <<1>>

We want the BNE instruction to point to STA 2:18, thus writing into 2:18 thirty times and causing the VM to run for about 3 seconds (plus the time spent for initialization).

Type in the code starting from 3:0, leaving the operand of BNE to 0. Then switch to NAV ASM mode. The listing should look like this:

LDX #30
STA 2:18
DEX
BNE !!!!

The BNE is pointless as it is (no matter the result of DEX, no branching takes place) and !!!! is a reminder that you probably meant to change it later on.

Here are the steps:

1. Make sure that the cursor is on BNE !!!!

2. Press 7. This sets an invisible marker on this address (3:6).

3. Move the cursor to STA 2:18 (i.e., 3:2)

4. Press 0. This swaps the cursor and the marker. The cursor is now on the BNE !!!! instruction and the invisible marker is now on the STA instruction.

Select PutMark from the menu.

The listing should now look like this:

LDX #30
STA 2:18
DEX
BNE 3:2

With the cursor still on the BNE instruction, press 9. This moves the cursor to the address pointed by the operand and can be handy to check if the operands are correct.

If you run the program, it should last for a bit more than 3 seconds and then terminate.

As an exercise, type in the GAMESET sequence, followed by:

LDY #10
LDX #8      [1]
LDA #2
STA 2:91
STA 2:18    [2]
DEX
BNE <<2>>
LDX #2
LDA #3
STA 2:91
STA 2:18    [3]
DEX
BNE <<3>>
DEY
BNE <<1>>

Key presses (OBSOLETE)

When you are confident in writing animations, you can start making programs that react to the user (i.e. interactive programs).

Empty includes a handy subroutine. It takes care of updating the display and saving in the Accumulator the code of the key that the user has pressed.

What is a subroutine? It is a piece of code that can be used again and again. You don't even have to understand how the piece of code works to be able to use. You "call" the subroutine (using JSR), and, when the subroutine has finished to do its job, the control returns to your code.

You can call the subroutine above with:

JSR 3:3

You can then check if a key has been pressed (BNE) or not (BEQ). For example, to wait for the user to press two keys, you could write something like this:

JSR 3:3     [1]
BEQ <<1>>
JSR 3:3     [2]
BEQ <<2>>

If the user has pressed a key, you can inspect the Accumulator to find out which key it was. The key codes are the same as the ones you use to write into 2:40 - 2:79. As a reminder, on a standard keypads they are:

key|value
---+-----
# | 35
* | 42
0 | 48
1 | 49
. . ..
. . ..
9 | 57

On a QWERTY, you might receive other codes too (e.g. 65 or 97 if the user presses the 'A' key).

Here is a more complex example. Press '1' or '3' to change a tile on the screen, and '*' to terminate the program.

JSR 3:3     [1]
BEQ <<1>>
CMP #49
BNE <<2>>
DEC 2:91
JMP <<1>>
CMP #51     [2]
BNE <<3>>
INC 2:91
JMP <<1>>
CMP #42     [3]
BNE <<1>>

If you have problem understanding it at first, you can set a break point on the instruction CMP #49 (i.e. from within the editor, go to 4:5 and select SetBrkPt before running the program). Once you press a key, the program is stopped and you can start pressing '1' to see what happens. If you are lost, you can press '*' to resume the program and try again with another key.

IO Programming (2)

In this page, the color codes of the standard palette are used throughout. As a quick reminder, here is a subset of them (see ioref for the complete list):

STD PALETTE
--+--------
0|BLACK
1|WHITE
2|RED
3|CYAN
4|PURPLE
5|GREEN
6|BLUE
7|YELLOW

IO Requests

The IO Chip (MIDP1/MIDP2) is able to execute some sort of instructions called "requests". For example, this is a request:

17 0

and it means "set the background color to BLACK".

This is another one:

17 0 0 255

and it means "set the background color to pure blue (i.e. RGB 0,0,255)".

While this one:

17 0 0

is an invalid request.

The first byte of a request (17 in the examples above) tells you what the request is all about ("set the background color", or SETBGCOL in the examples above).

Unlike CPU instructions, requests are "sent" to the IO Chip. Also, unlike CPU instructions you cannot tell how long is a request by looking at its first byte.

Suppose you want to send the request 17 5 to the IO Chip to set the background color to GREEN. First you send one byte after the other by writing into REQPUT (2:1):

LDA #17
STA 2:1
LDA #5
STA 2:1

Then you signal the end of the request by writing any value you like into REQEND (2:2):

STA 2:2

Now, suppose you want to send the request 17 20 100 255. Using 2:1 and 2:2, that would take 9 (i.e. 4*2 + 1) assembly instructions. There is another way. You can store a sequence of bytes like this (note the first two bytes stating how long the request is; in this case 4 bytes):

4 0 17 20 100 255

starting from, for example, 7:50 and then send the request writing the address (including the 2-bytes length) into the registers REQPTRHI (2:5) and REQPTRLO (2:4):

LDA #7
STA 2:5
LDA #50
STA 2:4

The order is important! The sending starts when you write into 2:4. When sending multiple requests, you don't need to rewrite into 2:5 if it doesn't change.

This way of sending a request by telling the IO chip where it starts in memory is called DMA (for Direct Memory Access).

Enable

When you write into FRMDRAW (2:18), the CPU is blocked until the next frame is due and the following elements are drawn:

1) A rectangle covering the whole frame using the color set by the request SETBGCOL (default: WHITE)

2) The image configured by the request SETBGIMG (default: no image)

3) The matrix of characters set by CONVIDEO, CONCCHR, CONCFG, etc..., a.k.a. the Console

4) Sprites and Tiled Layers configured by the requests LSPRITE/LTILED, a.k.a. the Layers

You can configure which of the steps above are actually carried out by adding together a combination of the following:

ENABLE
-+-------
1|BGCOL
2|BGIMG
4|CONSOLE
8|LAYERS

and writing the result into ENABLE (2:16).

For example, by default, the value stored in 2:16 is 5. 5 is 1+4. So, by default, the background and the console are drawn.

As a last example, this program:

LDA #17
STA 2:1
LDA #5
STA 2:1
STA 2:2

results in a green screen with a white rectangle in the middle (i.e. the console), while this program:

LDA #1
STA 2:16
LDA #17
STA 2:1
LDA #5
STA 2:1
STA 2:2

results in just a green screen, because the console has been disabled.

Creating images

The main request to create an image (IPNGGEN) is fairly complex and is usually generated by the Paint module. However, studying how that request is composed can help you to demystify the whole concept of images. Here is a 20 bytes request to create a small image.

(20 0)
25 1 8 0 8 0 1
3 3 1 2 7
126 255 219 255
195 231 255 126

I prefixed the request with a 2-bytes length (20 0) because it is common to send images to the IO chip using DMA.

First there is IPNGGEN (25), followed by the ImageId (1). The ImageId is discussed later. As long as you are consistent within your program, 1 is usually fine.

Then there are 4 bytes for the width (8 0) and the height (8 0) of the image, followed by the Depth (1) of the image. For Depth you can choose one of the following:

D|Col
-+---
1|  2
2|  4
4| 16
8|256

So, a Depth of 1 means 2 colors.

The next byte (ColorType) is usually 3, meaning that a color palette is included in the image. The next byte (Flags) is a combination of the following:

FLAGS
--+----------
1|IDX0TRANSP
2|PALREF
4|ZOOM0
8|ZOOM1
16|ZOOM2

On the example above, Flags is 3, meaning that the first color of the image is unused / transparent (IDX0TRANSP) and colors are described by the color codes of the current palette (PALREF). ZOOM0, ZOOM1 and ZOOM2 are discussed later.

Now the description of how the image looks like begins.

First there is the palette of the image:

1 2 7

The first byte (1) is the number of entries minus 1. So, the first 1 means that the palette has 2 entries (same as the number of colors; but it could be lower). The next 2 bytes are the color codes.

So, the first entry is RED and the second entry is YELLOW. However, since IDX0TRANSP above, RED pixels appear transparent.

If Flags did no contain PALREF, each entry would take 3 bytes instead of 1, for the red, green and blue components of the color.

Finally, there is group of bytes (Data) describing each pixel of the image.

126 255 219 255
195 231 255 126

Here is where these 8 bytes come from. If you draw a picture using X for YELLOW and . for RED:

.XXXXXX.
XXXXXXXX
XX.XX.XX
XXXXXXXX
XX....XX
XXX..XXX
XXXXXXXX
.XXXXXX.

And then turn them into 0s and 1s:

01111110
11111111
11011011
11111111
11000011
11100111
11111111
01111110

You can use the following table:

LO|  % |$| HI
--+----+-+---
0|0000|0|  0
1|0001|1| 16
2|0010|2| 32
3|0011|3| 48
4|0100|4| 64
5|0101|5| 80
6|0110|6| 96
7|0111|7|112
8|1000|8|128
9|1001|9|144
10|1010|A|160
11|1011|B|176
12|1100|C|192
13|1101|D|208
14|1110|E|224
15|1111|F|240

Here is how it works:

1) Take the first row of the image (01111110)

2) Split it in two halves (also called "nibbles"): 0111 and 1110.

3) Find the first half (0111) in the table, and note the number on the right (112).

4) Find the second half (1110) in the table, and note the number on the left (14).

5) Add the two numbers together: 112 + 14 = 126.

If you check, you will see that 126 is the first of the 8 Data bytes. Use the second row for the second byte, and so on.

Showing images

Once you create an image, to show it you can send a SETBGIMG (19) request. Remember to use ENABLE (2:16) to disable the console and enable the background image.

First create a program of 2 pages (1 page of code and 1 page of data).

At 4:0, create a IPNGGEN request (see above for the explanation):

20 0
25 1 8 0 8 0 1
3 3 1 2 7
126 255 219 255
195 231 255 126

At 4:22, create a SETBGIMG request:

2 0 19 1

The byte following 19 is the ImageId. As stated above, use 1 and you will be fine.

Now for the code (starting from 3:0).

First, use ENABLE to enable the background image and to disable the console:

LDA #3
STA 2:16

All the requests are on page 4, so setup the DMA to use page 4:

LDA #4
STA 2:5

Now create the image (send the IPNGGEN request):

LDA #0
STA 2:4

And show it (send the SETBGIMG request):

LDA #22
STA 2:4

If you try the program, you should see a yellow image centered on the screen. Keep this program; it will be modified below to explore other aspects of the IO chip.

References and slots

Images (and layers, to be discussed later) are "objects" managed by the IO chip.

You can keep references to images using slots. By default, there are 4 (image) slots (from 0 to 3) available.

When you create an image, ImageId is the slot you use. The slot will hold a reference to the image. If the slot already referenced another image, that reference is removed.

The example above used ImageId 1 and looked liked this:

IPNGGEN 1 (image A)
SETBGIMG 1

After these requests, the IO chip looks like this:

ID Slots     Images       Users
+---+
0 | . |
+---+      +-+
1 | -------> |A| <------ BGIMG
+---+      +-+
2 | . |
+---+
3 | . |
+---+

The request IDESTROY (20) removes a reference from a slot to an image. It is followed by the ImageId of the slot.

Note that in the example above you could send a IDESTROY with ImageId 1 and the image would not be destroyed (BGIMG "keeps it alive"). A better name for IDESTROY might have been something like IUNLINK.

Here is a more complex example. After the following requests:

IPNGGEN 3 (image A)
IDESTROY 3
IPNGGEN 0 (image B)
SETBGIMG 0
IPNGGEN 0 (image C)

The IO chip looks like this:

ID Slots     Images       Users
+---+    +-+
0 | -----> |C|
+---+    +-+
1 | . |          +-+
+---+          |B| <-- BGIMG
2 | . |     +-+  +-+
+---+     |A|
3 | . |     +-+
+---+

Image A is not used by anyone and the IO chip might destroy it if it needs more memory (a process called "garbage collection"). Image B is used by the IO chip (even if you have disabled the drawing of the background image with ENABLE). Image C is kept alive by the programmer, who is holding a reference to it in slot 0.

You can increase (or decrease) the number of image slots by sending a IDIM (21) request, but this is usually not necessary.

Layers (Sprites)

Layers are objects that you can easily move around the display. The name comes from the fact that when they overlap is as if they were stacked on top of each other.

You manage layers using slots. It is just like images, but it is a different set of slots. By default, there are 16 (layer) slots (from 0 to 15) available. If you need to increase the number of layers you can use the request LDIM (35). It is followed by the last layer id (i.e. 31 if you need 32 layers).

There are two kind of layers: sprites and tiled layers. Sprites are described first, as they are a bit easier to use. To create a sprite, you can use the LSPRITE (37) request. It is followed by the LayerId and by the ImageId. If you only use one sprite, you should use slot 0 for LayerId. How to handle multiple sprites is discussed later.

Starting from the program from the "Creating images" section above, you can replace the SETBGCOL request with a LSPRITE request. So, starting from 4:22, you will have:

3 0 37 0 1

The value written into ENABLE should also be changed. You want to enable the drawing of the layers instead of the background image. So, starting from 3:0, you will have:

LDA #9
STA 2:16

If you try the program now, nothing happens. This is because layers are created hidden. To show them, you have to write 128 into LCTL (2:81). So, at the end of the program (it should be at 3:20), add:

LDA #128
STA 2:81

If you try the program now, you should see the sprite. Unlike the background image, the sprite is positioned at the top left corner of the display. But unlike background images, you can move sprites! Use LX (2:82) and LY (2:83). You can of course do so in a loop. For example, at the end of the program (it should be at 3:25), add:

LDA #50
STA 2:83
INC 2:82
STA 2:18
JMP 3:30

As you can see, after the position of the sprite has been updated, the program writes to FRMDRAW (2:18).

Multiple Sprites

To create more than one sprite, just send more LSPRITE requests. Make sure to give each sprite a different LayerId. You can use the same image several times or give each sprite a separate image.

You can only control one layer (remember that sprites are just a type of layer) at a time. To tell the IO chip which one you want to control, write its LayerId into LID (2:80). The layer registers (i.e.: LCTL, LX, LY, etc...) will then refer to that particular layer. Initially, LID is set to 0; this is the reason why you don't need to worry about it if you only have one layer and its LayerId is 0.

Here is how to modify the previous example to handle multiple sprites.

Leave the data page as it is. You should have a IPNGGEN request at 4:0 and a LSPRITE request at 4:22.

The beginning of the code is left unchanged:

LDA #9
STA 2:16
LDA #4
STA 2:5

Then (starting at 3:10), the following loop creates 3 sprites:

LDX #0
LDA #0
STA 2:4
STX 4:25
LDA #22
STA 2:4
STX 2:80
LDA #128
STA 2:81
DEC 4:13
INX
CPX #3
BNE 3:12

Note how the requests are changed before they are sent to the IO chip: at each iteration, the color code of the image to be created (4:13) is decremented, and the LayerId (4:25) of the sprite to be created is incremented. Also note that LID (2:80) is updated with the LayerId of the newly created sprite, before enabling it by writing 128 into LCTL (2:81).

After the code above, the IO chip looks like this:

IMAGES                    LAYERS
ID Slots     Images       Slots ID
+---+          +-+     +---+
0 | . |          |Y| <------ |  0
+---+    +-+   +-+     +---+
1 | ----+  |B| <------------ |  1
+---+ |  +-+   +-+     +---+
2 | . | +------> |G| <-------|  2
+---+          +-+     +---+
3 | . |                    .
+---+                    .
+---+
| . | 15
+---+

If you try the program now, you will see only one sprite. The other two sprites are really there; they are simply below the one you see.

Add this loop at the end of the program (it should be at 3:41), to move them:

LDA #0
STA 2:80
INC 2:82
INC 2:80
INC 2:83
INC 2:80
INC 2:82
INC 2:83
STA 2:18
JMP 3:41

Note how at each frame LID (2:80) is first reset to select sprite 0 and then it is incremented twice to select the other two sprites. For each sprite, LX (2:82) and LY (2:83) are changed differently.

If you run the program now, the three sprites should move in a loop.

More on images

When converting an image to bytes, turning X/.s into 1/0s was a trivial, but important step. X/.s are pixels. 1/0s are bits. The distinction will become relevant shortly, when multicolor images are discussed, but first, let's see how images of sizes other than 8x8 are converted.

If the image is shorter or taller, it is not really a problem: a 8x2 image would just have 2 rows and a 8x100 image would just have 100 rows.

If the image is narrower, you fill each row until you reach 8 bits. For example, the following 3x3 image:

010
111
010

should be treated like this:

01000000
11100000
01000000

Note that the width to use in the IPNGGEN request is still 3.

If the image is wider, you need more bytes for each row; just group the bits in groups of 8 when you convert them. You still need to fill each row until you reach a multiple of 8 bits. For example, the following 10x3 image:

1111111111
1000000001
1111111111

should be treated like this:

11111111 11000000
10000000 01000000
11111111 11000000

Every byte of each row is sent starting from the first on the left, to the last on the right. To clarify, the Data of last image is 6 bytes long, and the order of the bytes is the following:

1 2
3 4
5 6

If the image has more than 2 colors, Depth means how many bits each pixel takes. Here is an example with Depth 2:

#| %|Color
-+--+------
0|00|WHITE
1|01|RED
2|10|YELLOW
3|11|GREEN

The following image:

RR..GG
..YY..

can be turned into the following bits:

01 01 00 00 11 11
00 00 10 10 00 00

The bits can then be regrouped and each row can be extended to reach the first multiple of 8:

01010000 11110000
00001010 00000000

Here is the resulting request:

(18 0)
25 1 6 0 2 0 2
3 3 3 1 2 7 5
80 240 10 0

If Depth is 4, the order of the bit patterns matching the image palette can be found on the table you used to convert bits to bytes.

When you start using lots of images, available data pages can run out quickly. This is especially a problem with phones with a high resolution display. The IPNGGEN request for a 16 colors, 16x16 image takes up more than half a page... and 16x16 might be quite small on a tiny 320x240 display!

You can add one of the following values (ZOOM0/ZOOM1/ZOOM2) to Flags:

ZOOM
--+--
0|x1
4|x2
8|x3
12|x4
16|x5
20|x6
24|x7
28|x8

If, for example, you create a 8x8 image with a zoom factor of x3 (+8), the actual image is going to be a 24x24 image. Note that, while this helps you to keep your program small, inside the IO chip the image still consumes as much memory as a regular 24x24 image. On the other hand, phones with high resolution displays usually have lots of memory, so this might be an acceptable trade off.

For the ZOOM flags to work, the width of the original image must be a multiple of 8.

Another use of the ZOOM flags is to create a background tileset. If you create a 8x1 RGB image with a zoom factor of x8 (+28), the resulting image will be a 64x8 images that can be used for a 8x8 LTILED (discussed later). Here is an example request:

(42 0)
25 1 8 0 1 0 8 3 28
7
0   0   0 ; BLACK
255 255 255 ; WHITE
255   0   0 ; RED
0 255   0 ; GREEN
0   0 255 ; BLUE
255 255   0 ; YELLOW
0 255 255 ; CYAN
255   0 255 ; MAGENTA
0 1 2 3 4 5 6 7

This is one of the few cases where a Depth of 8 is useful: you don't need to use binary numbers for Data, as every pixel is exactly 1 byte (i.e. 8 bits), and the Data section is still small as the original image is small (i.e. only 8x1 pixels).

Modules

Paint

Here is a short, partial and terse description of the Paint module. Practice first by loading a demo with images in it (e.g. vintage) and selecting an image from the list.

You can move the red square and then press fire to change the pixels of the image one by one. On some old phones, moving the cursor can be really slow. If this is the case, selecting the "(No)Coords" item from the menu should help.

There are two major modes: image and palette. You can switch between the two by pressing 0.

There are two sub modes for the image mode: pixel and tile. Tile mode is only available if the the dimensions of the image are multiple of 8. You can switch between the two by pressing #.

Image/pixel mode: 2, 4, 6, 8: move the cursor, 5: change the current pixel, 1: cycle foreground color, 3: cycle background color, 7: pick foreground color, 9: pick background color.

Image/tile mode: 2, 4, 6, 8: move the cursor, 1: cycle foreground color, 3: cycle background color, 7: copy tile, 5: paste/clear tile. Be careful when you use tile mode, as no undo is available and it is easy to clear entire tiles.

You can test how the program looks with the edited images, by pressing *. When the program ends, control is returned back to Paint.

IO Reference

Registers

REQPUT (1) [W]

REQEND (2) [W]

Send a request to the IO chip byte by byte. Each byte of the request is sent by writing it into REQPUT. The request is processed when any value is written into REQEND.

REQRES (3) [R]

Result of the last issued request. 0 on success, != 0 otherwise. To simplify the code, it is common not to test it.

REQPTRLO (4) [W]

REQPTRHI (5) [RW]

Send a request to the IO chip using DMA (Direct Memory Access). When a value is written into REQPTRLO, the two registers (LO for the offset, HI for the page) are used to point (PTR, for PoinTeR) to an area in memory where the request is stored. The area begins with two bytes (least significant byte first) storing the length of the request.

ENABLE (16) [RW]

Enable the drawing of: the background color, the background image, the console, and the layers. Default is 5 (1+4, BGCOL+CONSOLE).

ENABLE
-+-------
1|BGCOL
2|BGIMG
4|CONSOLE
8|LAYERS

FRMFPS (17) [RW]

Number of frames per seconds multiplied by 4 (e.g. 40, the initial value, is 10 fps).

FRMDRAW (18) [W]

Writing into FRMDRAW causes the CPU to be suspended until the current frame has been drawn.

GKEY0 (19) [RW]

GKEY1 (20) [R]

First write into GKEY0 to update the game keys latches, then read GKEY0/GKEY1 to find out the status of the gamekeys.

GKEY0
--+-----
2|UP
4|LEFT
32|RIGHT
64|DOWN

GKEY1
--+----
1|FIRE
2|A
4|B
8|C
16|D

RANDOM (23) [RW]

On read: get a random number <= n (255 by default). On write: set n (if > 0), or swap the number generator (if == 0). There are two number generators: one (used by default) is initialized using the time at the start of the program, the other is initialized with a constant.

KEYBUF (24, 8 bytes) [RW]

The standard KeyPresses (the ones that can be represented by ASCII codes; usually only 0-9, # and *) are enqueued starting from KEYBUF; the rest of the buffer is filled with 0s. Write into KEYBUF to consume a KeyPress. If the buffer is full when a new key is pressed, that KeyPress is lost.

CONCOLS (32) [RW]

CONROWS (33) [RW]

On read: get the dimension of the console (default: 10x4). On write: resize the console (0 = max size).

CONCX (34) [RW]

CONCY (35) [RW]

The console cursor (current console cell). CX < CONCOLS and CY < CONROWS. CONCCHR, CONCFG and CONCBG are relative to this cell.

CONCCHR (36) [RW]

The character code of the current console cell. Codes 32-126 are standard ASCII codes. Codes 161-255 are standard Latin1 codes. Codes 128-143 are non- standard line art codes (see bitmask below).

0 1 2 3 4 5 6 7 8 9
30       ! " # $ % & '
40 ( ) * + , - . / 0 1
50 2 3 4 5 6 7 8 9 : ;
60 < = > ? @ A B C D E
70 F G H I J K L M N O
80 P Q R S T U V W X Y
90 Z [ \ ] ^ _ ` a b c
100 d e f g h i j k l m
110 n o p q r s t u v w
120 x y z { | } ~

T    +-+-+-+-+-+-+-+-+
T    |1|0|0|0|B|R|L|T|
LLLXRRR +-+-+-+-+-+-+-+-+
B     1 6 3 1 8 4 2 1
B     2 4 2 6
8

CONCFG (37) [RW]

CONCBG (38) [RW]

The foreground and background color of the current console cell. Here are the standard color codes:

#|RED|GRN|BLU|NAME
--+---+---+---+----------
0|  0|  0|  0|BLACK
1|255|255|255|WHITE
2|190| 26| 36|RED
3| 48|230|198|CYAN
4|180| 26|226|PURPLE
5| 31|210| 30|GREEN
6| 33| 27|174|BLUE
7|223|246| 10|YELLOW
8|184| 65|  4|ORANGE
9|106| 51|  4|BROWN
10|254| 74| 87|LIGHTRED
11| 66| 69| 64|GRAY1
12|112|116|111|GRAY2
13| 89|254| 89|LIGHTGREEN
14| 95| 83|254|LIGHTBLUE
15|164|167|162|GRAY3

CONVIDEO (40, 40 bytes) [RW]

Quick access to the character codes of the topmost/leftmost 10x4 cells of the console.

LID (80) [RW]

The ID of the current layer.

LCTL (81) [RW]

Control register of the current layer. SHIFTX0 (1) and SHIFTX1 (2) are used to shift its X offset (see LX). SHIFTY0 (4) and SHIFTY1 (8) are used to shift its Y offset (see LY). PXLCOLL (16) is set if collision detection should be pixel perfect (more accurate, but slower); if unset, its collision rectangle is used (approximate, but faster). ENABLE (128) is set if the layer is visible.

LX (82) [RW]

LY (82) [RW]

The offset added to the position of the current layer, shifted by LCTL:SHIFTX and LCTL:SHIFTY bits to the left. For example, if LX is 3 and SHIFTX is 2 (i.e. SHIFTX0 is 0 and SHIFTX1 is 1) the offset is 3 << 2 = 12.

SFRAME (84) [RW]

If the current layer is a sprite, its active frame.

STRANSFM (85) [RW]

If the current layer is a sprite, its current transformation relative to its reference point. Valid values are: NONE (0), ROT90 (5), ROT180 (3), ROT270 (6), MIRROR (2), MROT90 (7), MROT180 (1), MROT270 (4).

SCWITH (86) [RW]

Write the ID of a layer to check if the current layer collides with it; then read the result (0 means no collision).

TCOLLO (87) [RW]

TCOLHI (88) [RW]

TROWLO (89) [RW]

TROWHI (90) [RW]

If the current layer is a tiled layer, the current tile cell (HI*256+LO).

TCELL (91)

The tile id of the current tile cell.

REQDAT (96, 32 bytes)

Return values for some IO requests.

Requests

Here below you can find the syntax of the requests (and the corresponding results, when applicable). See the bgcol1 and bgcol2 demos for examples of how to send a request to the IO chip. After the request has been sent, REQRES contains 0 on success and 255 on failure (usually not tested). Results are available starting from REQDAT. Streaming requests (identified by a >) are not bounded. The other requests are bounded (255 bytes).

Optional values are delimited by [ and ]. ( and ) are used for grouping. * means repeat 0 or more times. + means repeat at least once. # means repeat with constraints. | means choice (priority is low). Datatype is U8 unless stated otherwise (by a tag preceded by :). For datatypes larger than 8 bits, the least significat byte comes first. Enumerated values are identified by C (for choice). Bitmasks are identified by O (for OR). Strings can be delimited by 0 (S0) or by the end of the request (S). When an argument has datatype T, the actual datatype is chosen by the user with DType: U8 (1), I8 (2), U16 (3) and I16 (4).

For the semantic of the requests, take a look at the demos or simply experiment using names as hints. Notes for the IPNGGEN request: using INDEXED COLOR causes a PLTE chunk to be generated (a pallette must be provided) and setting IDX0TRANSP causes a tRNS chunk to be generated. For more information see the PNG specification.

System

TIME(2)

Get the elapsed time either since the epoch (1 Jan 1970) or the time the IO chip was reset. Resolution ranges from milliseconds to seconds.

Syntax: TIME(2) [RefTime=ABS [Fract=1000]] ; RefTime(C): ABS(1), RESET(2) ; Fract(C): 1(1), 10(2), 10(3), 1000(4) ; Result: Time:U64

LOADROM(6)

Load (part of) a ROM resource into memory. At the moment, only "cga.rom" is available.

Syntax: LOADROM(6) Addr:U16 ResName:S0 [Offset:U16 Size:U16]

RSFORMAT(8)

Delete every record from the store. Programs are kept.

Syntax: RSFORMAT(8) 121 33

RLOAD(9)

Load a record from the store. At most Size bytes are read.

Syntax: RLOAD(9) Addr:U16 Size:U16 RecName:S0

RSAVE(10)

Save a record into the store.

Syntax: RSAVE(10) Addr:U16 Size:U16 RecName:S0

RDELETE(11)

Delete a record from the store.

Syntax: RDELETE(11) RecName:S0

Display and Imaging

DPYINFO(16)

Get information about the display.

Syntax: DPYINFO(16) ; Result: Width:U16 Height:U16 ColorDepth AlphaDepth Flags ; Flags(O): ISCOLOR(128), ISMIDP2(64)

SETBGCOL(17)

Set the background color. Only used if BGCOL is set in the ENABLE register.

Syntax: SETBGCOL(17) PaletteEntry | Red Green Blue

SETPAL(18)

Set the current palette. An empty palette resets the standard one.

Syntax: SETPAL(18)> (Red Green Blue)*

SETBGIMG(19)

Set the background image. Only used if BGIMG is set in the ENABLE register. Once the request returns, the source image slot can be released / reused.

Syntax: SETBGIMG(19) ImageId

IDESTROY(20)

Release a specific image slot.

Syntax: IDESTROY(20) ImageId

IDIM(21)

Define which image slots are available (0..MaxImageId, initially 0..3).

Syntax: IDIM(21) MaxImageId

IINFO(22)

Get information about an image.

Syntax IINFO(22) ImageId ; Result: Width:U16 Height:U16 Flags ; Flags(O): ISMUTABLE(128)

ILOAD(23)

Load an image from a resource included in the jar.

Syntax: ILOAD(23) ImageId ResName:S0

IDUMMY(24)

Create a dummy image with a user defined pattern. Might be useful for testing. Bg is the background color, Fg is the foreground color, Title is a short text used as a label. Width and Height refer to the size of a single Frame/Tile. Tiles can be spans of different colors.

Syntax(1): IDUMMY(24) ImageId Type=SIMPLE Width:U16 Height:U16 Bg Fg [Title:S] ; Syntax(2): IDUMMY(24) ImageId Type=SPRITE Width Height Frames Bg Fg [Title:S] ; Syntax(3): IDUMMY(24) ImageId Type=TILES Width Height Cols Rows Bg Fg (N Bg Fg)* ; Type(C): SIMPLE(1), SPRITE(2), TILES(3)

IPNGGEN(25)

Generate an image using a packed representation. Data is a sequence of rows, each row is a sequence of pixels and each pixel is a sequence of bits. Each row is padded to a whole byte. If INDEXED COLOR is used, pixels are pointers to a palette preceding Data. If PALREF is set, palette entries are references to the current palette defined by the request SETPAL (or the standard palette). If IDX0TRANSP is set, ColorType must be INDEXED COLOR and pixels 0 are treated as transparent and the first palette entry is ignored.

Syntax: IPNGGEN(25)> ImageId Width:U16 Height:U16 Depth ColorType Flag [MaxPaletteEntry (PaletteEntry | Red Green Blue)#] Data# ; ColorType(C): GRAYSCALE(0), TRUECOLOR(2), INDEXED COLOR(3), GRAYSCALE ALPHA(4), TRUECOLOR ALPHA(6) ; Flags(O): IDX0TRANSP(1), PALREF(2), ZOOM0(4), ZOOM1(8), ZOOM2(16)

IEMPTY(26)

Create an empty mutable image. Deprecated.

Syntax: IEMPTY(26) ImageId Width:U16 Height:U16

IMKIMMUT(27)

Make an image immutable. Deprecated.

Syntax: IMKIMMUT(27) ImageId

IRAWRGBA(28)

Generate an image using data in RGBA8 format. If ALPHA is set, the alpha component is meaningful. Might be faster than IPNGGEN. Only available on MIDP2 phones.

Syntax: IRAWRGBA(28)> ImageId Width:U16 Height:U16 Flags (Red Green Blue Alpha)# ; Flags(O): ALPHA(128)

Layers (Game API)

LMVIEW(32)

Setup the layer manager so that: 1) a tiled layer is centered; or 2) a window is displayed

Syntax: LMVIEW(32) TiledLayerId | DType [X:T Y:T] Width:T Height:T

LMPOS(33)

Set the position of the layer manager on the screen

Syntax: LMPOS(33) DType OX:T OY:T

LDESTROY(34)

Release a specific layer slot.

Syntax: LDESTROY(34) LayerId

LDIM(35)

Define which layer slots are available (0..MaxLayerId, initially 0..15).

Syntax: LDIM(35) MaxLayerId

LTILED(36)

Create a (tiled) layer. Each tile is TWidth x THeight pixels. Pixels for the tiles come from the source image. After the layer is created, the image can be destroyed. The layer is composed of Cols x Rows tiles. The last NAnimTiles ids (e.g. 254 and 255, NAnimTiles is 2) are dynamic and you can change the static id they point to using the LTLANIM request.

Syntax: LTILED(36) TiledLayerId ImageId TWidth THeight NAnimTiles DType Cols:T Rows:T

LSPRITE(37)

Create a (sprite) layer. Pixels for the sprite come from the source image. After the layer is created, the image can be destroyed. if Width x Height is provided, the sprite is composed of multiple frames. Use the SFRAME register to select the active one (initially 0).

Syntax: LSPRITE(37) SpriteId ImageId [Width Height]

LSETPOS(38)

Set the position of a layer.

Syntax: LSETPOS(38) LayerId DType X:T Y:T

LGETPOS(39)

Get the position of a layer.

Syntax: LGETPOS(39) LayerId ; Result: X:I32 Y:I32

LMOVE(40)

Add a relative value to the position of a layer.

Syntax: LMOVE(40) LayerId DType DX:T DY:T

LSETPRI(41)

Set the priority of a layer (initially equal to its ID). This can be a fairly slow request, and should be avoided during the main loop.

Syntax: LSETPRI(41) LayerId DType Priority:T

LGETPRI(42)

Get the priority of a layer.

Syntax: LGETPRI(42) LayerId ; Result: Priority:I32

LTLANIM(43)

Change the id of the dynamic tile (initially 0). This is a fairly fast request, and can be safetly used during the main loop.

System: LTLANIM(43): TiledLayerId AnimTile StaticTile

LTLFILL(44)

Fill (part of) a tiled layer with a specific tile. When created, a tiled layer is filled with 0 (empty tile).

Syntax: LTLFILL(44) TiledLayerId Tile [DType Col:T Row:T NumCols:T NumRows:T]

LTLPUT(45)

Fill part of a tiled layer with tiles provided at the end of the request.

Syntax: LTLPUT(45)> TiledLayerId Col:U16 Row:U16 NumCols:U16 Tile*

LTLSCRLL(46)

Scroll the tiles within a rectangular region of a tiled layer. New tiles are filled with 0 (empty tile).

Syntax: LTLSCRLL(46) TiledLayerId ScrollType=0 DType Col:T Row:T NumCols:T NumRows:T DX:T DY:T

LSPCOPY(47)

Create a (sprite) layer using an existing sprite as a template. On some phones, this might be more memory efficient than creating an idential sprite.

Syntax: LSPCOPY(47) SpriteId TemplateSpriteId

LSPAPOS(48)

Syntax: LSPAPOS(48) SpriteId DType AbsX:T AbsY:T

LSPREFPX(49)

Set the reference point of a sprite. Used as a pivot point for transformation and when setting the sprite position with LSPAPOS.

Syntax: LSPREFPX(49) SpriteId DType RefPixelX:T RefPixelY:T

LSPCLRCT(50)

Set the bounding box of a sprite. Used for approximate collision detection.

Syntax: LSPCLRCT(50) SpriteId DType CollRctOX:T CollRctOY:T CollRctWidth:T CollRctHeight:T

GAMESET(60)

Setup a tiled layer using an image or a pre-defined tile set, and make it visible and current. The tiled layer is used to center the view.

Syntax: GAMESET(60) [(ImageId | TileSet=SILK) [Cols=0 Rows=0 [LayerId=1 [TWidth=0 THeight=0]]]] ; TileSet(C): SILK(255), FONT(254), MICRO(253) ; Result: Cols:U16 Rows:U16

Effects

WARNING: The effect API is poorly supported on most phones, and in some cases it might cause JBit to freeze. Experiment only after you have saved your work!

FXTONE(64)

Play a tone for Duration * 10 milliseconds. Volume: 0-100.

Syntax: FXTONE(64) Duration Frequency Volume

FXVIBRA(65)

Enable the vibra function for Duration * 10 milliseconds.

Syntax: FXVIBRA(65) Duration ; Result: Supported

FXFLASH(66)

Enable the flashlight function for Duration * 10 milliseconds.

Syntax: FXFLASH(66) Duration ; Result: Supported