[
Home |
Documentation |
Gallery |
Download |
Demo |
Resources
]
[
Index |
Tutorial |
Sheets |
Pack |
JB |
Hello |
Paint |
MicroIO
]
This page describes a minimalistic IO chip. It can be used to save some memory on old phones or as a starting point to write a custom IO chip.
This is a work in progress; the code is complete, but the commentary is not available yet.
MicroIO (IO.class) implements the subset of the standard IO chip described in JBit-QS.
Start from the JBit source distribution (configured as Standard) and backup IO.java, if necessary.
Replace the content of IO.java with the following:
import javax.microedition.lcdui.*;
public final class IO extends Canvas implements Module {
}
We will now complete the IO class.
private Object getMetadata(int iArg) {
switch (iArg) {
case METADATA_ID_LABEL:
return "MicroIO";
case METADATA_ID_TYPE:
return new Integer(TYPE_VM_PLUGIN);
case METADATA_ID_SERVICES:
return new String[] { IOSvc.TAG };
default:
return null;
}
}
public Object opO(int opId, int iArg, Object oArg) {
switch (opId) {
case OP_GET_METADATA:
return getMetadata(iArg);
case OP_GET_DISPLAYABLE:
return this;
}
return null;
}
public int opI(int opId, int iArg, Object oArg) {
switch (opId) {
case OP_INIT:
return init();
case OP_ACTIVATE:
return activate();
case OP_DEACTIVATE:
return deactivate();
case IOSvc.OP_RESET:
return reset();
case IOSvc.OP_FLUSH:
return flush();
case IOSvc.OP_ALIGN:
return align(iArg);
case IOSvc.OP_DO_SOME_WORK:
return doSomeWork();
case IOSvc.OP_SET_ADDRESS_SPACE:
return setAddressSpace(oArg);
}
return -1;
}
public static final int REG_FRMFPS = 17;
public static final int REG_FRMDRAW = 18;
public static final int REG_KEYBUF = 24;
public static final int REG_CONVIDEO = 40;
private static final int KEYBUF_SIZE = 8;
private static final int COLS = 10;
private static final int ROWS = 4;
private static final int CONVIDEO_SIZE = COLS * ROWS;
public short get(int address) {
switch (address) {
case REG_FRMFPS:
return doFrmFpsGet();
default:
if (address >= REG_KEYBUF
&& address < REG_KEYBUF + KEYBUF_SIZE) {
return doKeyBufGet(address);
} else if (address >= REG_CONVIDEO
&& address < REG_CONVIDEO + CONVIDEO_SIZE) {
return doVideoGet(address);
}
}
return 0;
}
public short put(int address, short value) {
switch (address) {
case REG_FRMFPS:
doFrmFpsPut(value);
break;
case REG_FRMDRAW:
doFrmDrawPut(value);
break;
case REG_KEYBUF:
doKeyBufPut(value);
break;
default:
if (address >= REG_CONVIDEO
&& address < REG_CONVIDEO + CONVIDEO_SIZE)
doVideoPut(address, value);
}
return 0;
}
protected void paint(Graphics g) {
videoDraw(g);
}
private int init() {
return 0;
}
private int activate() {
return 0;
}
private int deactivate() {
return 0;
}
private int reset() {
frameReset();
keyBufReset();
videoReset();
return 0;
}
private int flush() {
repaint();
serviceRepaints();
return 0;
}
private int vmStatus = IOSvc.VM_STATUS_RUNNING;
private boolean signalUserBreak;
private boolean signalSuspendCPU;
private boolean signalResumeCPU;
private int align(int vmStatus) {
this.vmStatus = vmStatus;
if (signalUserBreak) {
signalUserBreak = false;
return IOSvc.ALIGN_USER_BREAK;
}
if (signalSuspendCPU) {
signalSuspendCPU = false;
return IOSvc.ALIGN_SUSPEND_CPU;
}
if (signalResumeCPU) {
signalResumeCPU = false;
return IOSvc.ALIGN_RESUME_CPU;
}
return IOSvc.ALIGN_ACTIVE;
}
private int setAddressSpace(Object oArg) {
return 0;
}
private static final int FRAME_MIN_WAIT = 10;
private int fps;
private long nextFrame;
private int frameInterval;
private boolean waitingForFrame;
private void frameReset() {
doFrmFpsPut((short)40);
}
private int doSomeWork() {
int wait = -1;
long now = System.currentTimeMillis();
if (nextFrame != 0 && now >= nextFrame - FRAME_MIN_WAIT) {
do
nextFrame += frameInterval;
while (nextFrame < now);
flush();
if (waitingForFrame) {
waitingForFrame = false;
signalResumeCPU = true;
}
wait = 0;
}
if (waitingForFrame) {
wait = (int)(nextFrame - now - (FRAME_MIN_WAIT >> 1));
}
return wait;
}
private short doFrmFpsGet() {
return (short)fps;
}
private void doFrmFpsPut(short value) {
fps = value;
if (value == 0) {
frameInterval = 0;
nextFrame = 0;
} else {
frameInterval = 4000 / value;
nextFrame = System.currentTimeMillis() + frameInterval;
}
}
private void doFrmDrawPut(short value) {
if (frameInterval == 0) {
flush();
} else {
signalSuspendCPU = true;
waitingForFrame = true;
}
}
private byte[] video = new byte[CONVIDEO_SIZE];
private void videoReset() {
for (int i = 0; i < CONVIDEO_SIZE; i++)
video[i] = ' ';
}
private void videoDrawLineArt(Graphics g, int fontWidth, int fontHeight,
int x, int y, int chr) {
int px = (fontWidth - 2) >> 1;
int py = (fontHeight - 2) >> 1;
g.fillRect(x + px, y + py, 2, 2);
if ((chr & 0x1) != 0)
g.fillRect(x + px, y, 2, py);
if ((chr & 0x2) != 0)
g.fillRect(x, y + py, px, 2);
if ((chr & 0x4) != 0)
g.fillRect(x + px + 2, y + py, fontWidth - px - 2, 2);
if ((chr & 0x8) != 0)
g.fillRect(x + px, y + py + 2, 2, fontHeight - py - 2);
}
private void videoDraw(Graphics g) {
g.setColor(0xFFFFFF);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(0x000000);
Font font = Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD,
Font.SIZE_SMALL);
int fontWidth = font.charWidth('W');
int fontHeight = font.getHeight();
int ox = (getWidth() - COLS * fontWidth) >> 1;
int oy = (getHeight() - ROWS * fontHeight) >> 1;
g.setFont(font);
int x, y = oy, index = 0;
for (int r = 0; r < ROWS; r++) {
x = ox;
for (int c = 0; c < COLS; c++) {
int chr = video[index++] & 0xFF;
if (chr != 0) {
if ((chr & 0xF0) == 0x80)
videoDrawLineArt(g, fontWidth, fontHeight, x, y, chr);
else
g.drawChar((char)chr, x, y, 0);
}
x += fontWidth;
}
y += fontHeight;
}
}
private void doVideoPut(int address, short value) {
video[address - REG_CONVIDEO] = (byte)value;
}
private short doVideoGet(int address) {
return (short)(video[address - REG_CONVIDEO] & 0xFF);
}
private byte[] keyBuf = new byte[KEYBUF_SIZE];
private synchronized void keyBufReset() {
for (int i = 0; i < KEYBUF_SIZE; i++)
keyBuf[i] = 0;
}
private synchronized void doKeyBufPut(short value) {
if (value == 0)
value = KEYBUF_SIZE;
for (int i = 0; i < KEYBUF_SIZE; i++)
if (i + value < KEYBUF_SIZE)
keyBuf[i] = keyBuf[i + value];
else
keyBuf[i] = 0;
}
private short doKeyBufGet(int address) {
return (short)(keyBuf[address - REG_KEYBUF] & 0xFF);
}
private synchronized void enqueKeyCode(int keyCode) {
for (int i = 0; i < KEYBUF_SIZE; i++)
if (keyBuf[i] == 0) {
keyBuf[i] = (byte)keyCode;
return;
}
}
protected void keyPressed(int keyCode) {
if (vmStatus == IOSvc.VM_STATUS_MONITOR || keyCode < 0)
signalUserBreak = true;
else
enqueKeyCode(keyCode);
}