This is a demo i did using the Z88DK to program NB in C.
Cmd line to compile this : zcc +newbrain -O3 -lndos -zorg=20000 -create-app test1.c
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <newbrain.h>
#include <stdbool.h>
// External graphics
extern char whitepiece[];
extern char blackpiece[];
extern char pacmanleft[];
extern char pacmanright[];
extern char tempspr[];
long heap; // For dynamic memory allocation
void initDynMem() {
mallinit();
sbrk(28000, 3000); // Allocate memory at 28000 size 1000 bytes used for malloc contains the graphics too
}
// NEWBRAIN SPECIFIC FUNCTIONS
// Opens some devices needed to operate
void setup() {
nb_close(5); // Close stream 5 if open
nb_open(INP, 5, 5, 5, ""); // Open device 5 for input reads a char from keyboard WAITS
nb_close(6); // Close stream 6 if open
nb_open(INP, 6, 6, 6, ""); // Open device 6 for input reads a char from keyboard RETURN IMMEDIATELY
}
void cleanExit() {
nb_close(5);
nb_close(6);
}
// Sets cursor at x, y position on screen
void scrPos(char x, char y) {
printf("%c%c%c", 22, x, y);
}
// Clears Text Screen
void cls() {
printf("%c", 31);
}
//waits for a char
char nbGetCharW() {
return nb_getc(5);
}
//returns immediately char or not
char nbGetChar() {
return nb_getc(6);
}
unsigned int textlinewidth;
unsigned int graphlinewidth;
//opens a text screen 80 or 40 cols with or without vfd
void nbSetScreen(int lines, bool is80cols, bool withvfd) {
char params[8];
int dv = withvfd ? 4 : 0;
if (is80cols) {
sprintf(params, "l%d", lines);
textlinewidth = 128;
graphlinewidth = 80;
} else {
sprintf(params, "%d", lines);
textlinewidth = 64;
graphlinewidth = 40;
}
nb_close(0);
nb_open(OUTP, 0, dv, 0, params);
}
unsigned int tvGraphLines;
unsigned int tvGraphBufferReal;
unsigned char* ram = 0; // Pointer to ram at Ram Address 0
void setGraphParams() {
const unsigned int tvramaddr = 0x5c;
unsigned int tvram = ram[tvramaddr] + (ram[tvramaddr+1] << 8);
unsigned char tvoffs = ram[tvram];
unsigned int tvbuffer = tvram + tvoffs + 5;
unsigned int tvtxtlinebufferlen = ram[tvram + 4] * textlinewidth;
unsigned int tvgraphbuffer = tvbuffer + tvtxtlinebufferlen;
tvGraphBufferReal = tvgraphbuffer + 10 * graphlinewidth;
}
//opens a graphic screen 640 or 320 px wide wide or narrow
void nbSetGraphScreen(int grlines, bool is80cols, bool withvfd, bool isnarrow) {
int neededtextlines = 200; // Should be calculated
char params[8];
nbSetScreen(neededtextlines, is80cols, withvfd);
nb_close(11);
sprintf(params, isnarrow ? "n%d" : "%d", grlines);
nb_open(OUTP, 11, 11, 11, params);
tvGraphLines = grlines;
setGraphParams();
}
// Graphics screen functions
typedef enum {
mvLEFT,
mvRIGHT,
mvUP,
mvDOWN,
mvLAST,
mvNONE,
} CMDMove;
struct TGraphic {
int x;
int y;
int w;
int h;
int prex;
int prey;
char speed;
bool doPaint;
CMDMove lastCmdMove;
char pnttimes; //count how many times we paint an image helps to make animation
char pntspeed;
char imgcount;
char curimg;
char *graphic;
char *sprite;
};
const char DEFSPEED = 1;
enum { MAXGRAPHICS = 5 };
struct TGraphic graphicArr[MAXGRAPHICS];
int grArrLen = 0;
int createGraphic(int w, int h, bool domemalloc, char imgcnt) {
if (grArrLen >= MAXGRAPHICS) {
return -1; // Array full
}
unsigned int grSize = (w/8) * h;
graphicArr[grArrLen].w = w;
graphicArr[grArrLen].h = h;
graphicArr[grArrLen].speed = DEFSPEED;
graphicArr[grArrLen].doPaint = true;
graphicArr[grArrLen].lastCmdMove = mvNONE;
graphicArr[grArrLen].pnttimes = 0;
graphicArr[grArrLen].pntspeed = 4;
graphicArr[grArrLen].imgcount = imgcnt;
graphicArr[grArrLen].curimg = 0;
if (domemalloc) {
graphicArr[grArrLen].graphic = (char*)malloc(grSize * imgcnt * sizeof(char));
if (!graphicArr[grArrLen].graphic) {
return -2; // Memory allocation failed
}
} else {
graphicArr[grArrLen].graphic = NULL;
}
//buffer used for scrolling the graphic in bytes
graphicArr[grArrLen].sprite = (char*)malloc((grSize+1*h) * sizeof(char));
return grArrLen++;
}
// Function to reverse the bits of an 8-bit integer
extern unsigned char reverse_bits(unsigned char n) __z88dk_fastcall;
#asm
; Z80 assembly code to reverse the bits of an 8-bit integer
; Inputs:
; A - 8-bit integer to reverse bits
; Outputs:
; A - Reversed 8-bit integer
._reverse_bits
; reverse bits in A
; 17 bytes / 66 cycles
;param on l due to fastcall so copy to a
ld a,l ; a = 76543210
rlca
rlca ; a = 54321076
xor l
and 0xAA
xor l ; a = 56341270
ld l,a
rlca
rlca
rlca ; a = 41270563
rrc l ; l = 05634127
xor l
and 0x66
xor l ; a = 01234567
ld l,a
ret ; Return with reversed byte in A
#endasm
#asm
defm "paintToSprite"
#endasm
void paintToSprite(struct TGraphic *mygr){
int posx = mygr->x / 8;
int posxrem = mygr->x % 8;
int posy = mygr->y;
int ibt = 0;
// int btaddr = tvGraphBufferReal + posy * graphlinewidth + posx;
int btwidth = (mygr->w ) / 8;
int imglen = btwidth * mygr->h;
unsigned char *tmgraph=mygr->graphic+(mygr->curimg*imglen);
int ispr = 0;
for (unsigned char y = 0; y < mygr->h; y++) {
unsigned char btx , btxnext = 0;
for (unsigned char x = 0; x < btwidth; x++) {
unsigned char btrd = tmgraph[ibt++];
btx = (btrd >> posxrem) | btxnext;
btxnext = btrd << (8 - posxrem);
mygr->sprite[ispr++]= reverse_bits(btx);
//ram[btaddr++] = reverse_bits(btx);
}
mygr->sprite[ispr++]= reverse_bits(btxnext);
//ram[btaddr] = reverse_bits(btxnext);
// btaddr += (graphlinewidth - btwidth );
}
mygr->doPaint = false;
if (++mygr->pnttimes>mygr->pntspeed) { //every x frames change image
mygr->pnttimes=0;
mygr->curimg = (mygr->curimg+1 < mygr->imgcount) ? mygr->curimg+1 : 0;
}
}
#asm
defm "moveGraphic"
#endasm
void moveGraphic(struct TGraphic *mygr, CMDMove cmdNewMv) {
if (cmdNewMv == mvLAST) cmdNewMv = mygr->lastCmdMove;
else mygr->lastCmdMove = cmdNewMv;
mygr->prex = mygr-> x;
mygr->prey = mygr-> y;
switch (cmdNewMv) {
case mvLEFT:
if (mygr->x-mygr->speed>0) {
mygr->x -= mygr->speed;
mygr->doPaint = true;
}
break;
case mvRIGHT:
if (mygr->x+mygr->w+mygr->speed<graphlinewidth*8) { //graphlinewidth is in bytes
mygr->x += mygr->speed;
mygr->doPaint = true;
}
break;
case mvUP:
if (mygr->y-mygr->speed>0) {
mygr->y -= mygr->speed;
mygr->doPaint = true;
}
break;
case mvDOWN:
if (mygr->y+mygr->h+mygr->speed<tvGraphLines) {
mygr->y += mygr->speed;
mygr->doPaint = true;
}
break;
case mvNONE:
break;
case mvLAST:
// Do nothing
mygr->doPaint = true;
break;
}
// if (mygr->doPaint)
paintToSprite(mygr);
}
#asm
defm "showGraphic"
#endasm
void showGraphic(struct TGraphic *mygr) {
//if (!mygr->doPaint) return;
int posx = mygr->x / 8;
int posxrem = mygr->x % 8;
int posy = mygr->y;
int ibt = 0;
int btaddr = tvGraphBufferReal + posy * graphlinewidth + posx;
int btwidth = (mygr->w ) / 8;
int imglen = btwidth * mygr->h;
for (unsigned char y = 0; y < mygr->h; y++) {
unsigned char btx = 0, btxnext = 0;
for (unsigned char x = 0; x < btwidth; x++) {
unsigned char btrd = (mygr->graphic+(mygr->curimg*imglen))[ibt++];
btx = (btrd >> posxrem) | btxnext;
btxnext = btrd << (8 - posxrem);
ram[btaddr++] = reverse_bits(btx);
}
ram[btaddr] = reverse_bits(btxnext);
btaddr += (graphlinewidth - btwidth );
}
mygr->doPaint = false;
if (++mygr->pnttimes>mygr->pntspeed) { //every x frames change image
mygr->pnttimes=0;
mygr->curimg = (mygr->curimg+1 < mygr->imgcount) ? mygr->curimg+1 : 0;
}
}
#asm
defm "showSprite"
#endasm
void showSprite(struct TGraphic *mygr) {
int posx = mygr->x / 8;
//int posxrem = mygr->x % 8;
int posy = mygr->y;
int ibt = 0;
int btaddr = tvGraphBufferReal + posy * graphlinewidth + posx;
int btwidth = (mygr->w ) / 8;
//int imglen = btwidth * mygr->h;
for (unsigned char y = 0; y < mygr->h; y++) {
unsigned char btx = 0, btxnext = 0;
for (unsigned char x = 0; x <= btwidth; x++) {
unsigned char btrd = mygr->sprite[ibt++];
ram[btaddr++] = btrd;
}
btaddr += (graphlinewidth - btwidth -1 );
}
mygr->doPaint = false;
}
void clearSprite(struct TGraphic *mygr) {
int posx = mygr->prex / 8;
int posy = mygr->prey;
int btaddr = tvGraphBufferReal + posy * graphlinewidth + posx;
int btwidth = (mygr->w ) / 8;
for (unsigned char y = 0; y < mygr->h; y++) {
for (unsigned char x = 0; x <= btwidth; x++) {
ram[btaddr++] = 0;
}
btaddr += (graphlinewidth - btwidth -1 );
}
}
int spridx=0;
bool painted = false;
void paintSprites() {
#asm
di
#endasm
for (unsigned char j = 0; j < grArrLen;j++)
clearSprite( graphicArr[j] );
for (unsigned char j = 0; j < grArrLen;j++)
showSprite( graphicArr[j] );
// showSprite(graphicArr[spridx++]);
// showGraphic(graphicArr[spridx++]);
//if (spridx>grArrLen) spridx=0;
#asm
ei
#endasm
painted = true;
}
int random_number(int min_num, int max_num) {
int result = 0, low_num = 0, hi_num = 0;
if (min_num < max_num) {
low_num = min_num;
hi_num = max_num + 1; // Include max_num in output
} else {
low_num = max_num + 1; // Include max_num in output
hi_num = min_num;
}
result = (rand() % (hi_num - low_num)) + low_num;
return result;
}
// Text screen functions
void testtextscreen() {
nbSetScreen(15, true, true); // Set new 15 lines text screen 80cols with VFD screen
cls(); // Clear screen
printf("%c\nHello from C TEST PROGRAM\r", 31);
printf("Hello from C TEST PROGRAM\r");
scrPos(1, 10); // Set cursor position to 1st column 10th line
printf("Position in line 10\r");
char c = nbGetCharW();
printf("You pressed: %c as int= %d \r", c, c);
nbGetCharW();
nbSetScreen(25, false, true); // Set default screen 24 lines
}
enum { nbleft = 8 , nbright = 26, nbup = 11, nbdown = 10 };
void doMovePlayer(char c){
char spr1=0;
switch (c) {
case nbleft:
graphicArr[spr1].graphic = pacmanleft;
moveGraphic(&graphicArr[spr1], mvLEFT);
break;
case nbright:
graphicArr[spr1].graphic = pacmanright;
moveGraphic(&graphicArr[spr1], mvRIGHT);
break;
case nbup:
moveGraphic(&graphicArr[spr1], mvUP);
break;
case nbdown:
moveGraphic(&graphicArr[spr1], mvDOWN);
break;
case ' ':
moveGraphic(&graphicArr[spr1], mvNONE);
break;
default: {
moveGraphic(&graphicArr[spr1], mvLAST);
}
}
}
void doMoveEnemy(){
char c=0;
char spr2=1;
int r = random_number(0, 100);
if (r > 96) {
int r2 = random_number(0, 100);
if (r2 < 25) c = 'z';
else if (r2 < 50) c = 'x';
else if (r2 < 75) c = 'q';
else c = 'a';
}
switch (c) {
case 'z': moveGraphic(&graphicArr[spr2], mvLEFT); break;
case 'x': moveGraphic(&graphicArr[spr2], mvRIGHT); break;
case 'q': moveGraphic(&graphicArr[spr2], mvUP); break;
case 'a': moveGraphic(&graphicArr[spr2], mvDOWN); break;
default: {
moveGraphic(&graphicArr[spr2], mvLAST);
}
}
}
char pr = 0;
bool tmelapsed(){
unsigned char c3, c2, c1;
char ne = 0;
c3 = ram[105];
c2 = ram[106];
c1 = ram[107];
//
ne = c1 & 4;
if (pr!=ne){
pr = ne;
return true;
}
return false;
}
int main() {
initDynMem(); // Initializing dynamic memory allocation
setup(); // Setup needed devices
//create a graphic screen 160 lines, low res, with vfd and wide
nbSetGraphScreen(160, false, true, false);
cls();
int spr1 = createGraphic(16, 16, false, 3);
printf("Graphic created at pos %d\r", spr1);
graphicArr[spr1].x = 10;
graphicArr[spr1].y = 20;
graphicArr[spr1].pntspeed = 2;
graphicArr[spr1].speed = 2;
graphicArr[spr1].graphic = pacmanleft;
//graphicArr[spr1].sprite = tempspr;
int spr2 = createGraphic(16, 16, false, 2);
printf("Graphic created at pos %d\r", spr2);
graphicArr[spr2].x = 80;
graphicArr[spr2].y = 60;
graphicArr[spr2].graphic = whitepiece;
int c, lastkey = 0;
do {
c = nbGetChar();
if (c=='d'){
printf("x=%d, y=%d, curimg=%d %d %d\r",graphicArr[spr1].x,graphicArr[spr1].y,graphicArr[spr1].curimg,graphicArr[spr1].pnttimes,graphicArr[spr1].imgcount);
}
else
if (c=='t'){
printf("Timer: %d %d\r", ram[105],pr);
}
else
if (c=='-'){
graphicArr[spr1].speed--;
printf("Speed: %d \r", graphicArr[spr1].speed);
}
else
if (c=='+'){
graphicArr[spr1].speed++;
printf("Speed: %d \r", graphicArr[spr1].speed);
}
else
if (c!=0)
lastkey = c;
if (painted) {
doMovePlayer(lastkey);
doMoveEnemy();
painted = false;
}
if (tmelapsed()){
if (!painted)
paintSprites();
}
} while (c != 27);
cleanExit(); // Close devices
nb_close(1); // Close tape the loader leaves it open
}
#asm
._whitepiece
defb @00000000, @00000000
defb @00000000, @00000000
defb @00000011, @11000000
defb @00001100, @00110000
defb @00010000, @11001000
defb @00010000, @00101000
defb @00100000, @00010100
defb @00100000, @00010100
defb @00100000, @00000100
defb @00100000, @00000100
defb @00010000, @00001000
defb @00010000, @00001000
defb @00001100, @00110000
defb @00000011, @11000000
defb @00000000, @00000000
defb @00000000, @00000000
._blackpiece
defb @00000000, @00000000
defb @00000000, @00000000
defb @00000011, @11000000
defb @00001111, @11110000
defb @00011111, @00111000
defb @00011111, @11011000
defb @00111111, @11101100
defb @00111111, @11101100
defb @00111111, @11111100
defb @00111111, @11111100
defb @00011111, @11111000
defb @00011111, @11111000
defb @00001111, @11110000
defb @00000011, @11000000
defb @00000000, @00000000
defb @00000000, @00000000
//defm "pacman"
._pacmanright
defb @00000000, @00000000
defb @00000000, @00000000
defb @00000011, @11000000
defb @00001111, @11110000
defb @00011111, @11100000
defb @00111111, @10000000
defb @00111110, @00000000
defb @00111100, @00000000
defb @00111100, @00000000
defb @00111110, @00000000
defb @00111111, @10000000
defb @00011111, @11100000
defb @00001111, @11110000
defb @00000011, @11000000
defb @00000000, @00000000
defb @00000000, @00000000
defb @00000000, @00000000
defb @00000000, @00000000
defb @00000011, @11000000
defb @00001111, @11110000
defb @00011111, @11111000
defb @00111111, @11111100
defb @00111111, @11000000
defb @00111100, @00000000
defb @00111100, @00000000
defb @00111111, @11000000
defb @00111111, @11111100
defb @00011111, @11111000
defb @00001111, @11110000
defb @00000011, @11000000
defb @00000000, @00000000
defb @00000000, @00000000
defb @00000000, @00000000
defb @00000000, @00000000
defb @00000011, @11000000
defb @00001111, @11110000
defb @00011111, @11111000
defb @00111111, @11111100
defb @00111111, @11111100
defb @00111111, @11111100
defb @00111111, @11111100
defb @00111111, @11111100
defb @00111111, @11111100
defb @00011111, @11111000
defb @00001111, @11110000
defb @00000011, @11000000
defb @00000000, @00000000
defb @00000000, @00000000
._pacmanleft
defb @00000000 , @00000000
defb @00000000 , @00000000
defb @00000011 , @11000000
defb @00001111 , @11110000
defb @00011111 , @11111000
defb @00111111 , @11111100
defb @00000011 , @11111100
defb @00000000 , @00111100
defb @00000000 , @00111100
defb @00000011 , @11111100
defb @00111111 , @11111100
defb @00011111 , @11111000
defb @00001111 , @11110000
defb @00000011 , @11000000
defb @00000000 , @00000000
defb @00000000 , @00000000
defb @00000000 , @00000000
defb @00000000 , @00000000
defb @00000011 , @11000000
defb @00001111 , @11110000
defb @00000111 , @11111000
defb @00000001 , @11111100
defb @00000000 , @01111100
defb @00000000 , @00111100
defb @00000000 , @00111100
defb @00000000 , @01111100
defb @00000001 , @11111100
defb @00000111 , @11111000
defb @00001111 , @11110000
defb @00000011 , @11000000
defb @00000000 , @00000000
defb @00000000 , @00000000
defb @00000000, @00000000
defb @00000000, @00000000
defb @00000011, @11000000
defb @00001111, @11110000
defb @00011111, @11111000
defb @00111111, @11111100
defb @00111111, @11111100
defb @00111111, @11111100
defb @00111111, @11111100
defb @00111111, @11111100
defb @00111111, @11111100
defb @00011111, @11111000
defb @00001111, @11110000
defb @00000011, @11000000
defb @00000000, @00000000
defb @00000000, @00000000
//temp use we malloc the necessary bytes
defm "TEMPBUFFER"
._tempspr
defb 0,0,0
defb 0,0,0
defb 0,0,0
defb 0,0,0
defb 0,0,0
defb 0,0,0
defb 0,0,0
defb 0,0,0
defb 0,0,0
defb 0,0,0
defb 0,0,0
defb 0,0,0
defb 0,0,0
defb 0,0,0
defb 0,0,0
defb 0,0,0
#endasm