vectrex: got xasm and c working
This commit is contained in:
parent
f1961bc9bf
commit
01b01b169a
|
@ -0,0 +1,22 @@
|
|||
|
||||
#pragma vx_copyright "2020"
|
||||
#pragma vx_title_pos -100, -80
|
||||
#pragma vx_title_size -8, 80
|
||||
#pragma vx_title "HELLO WORLD"
|
||||
#pragma vx_music vx_music_1
|
||||
|
||||
#include "vectrex.h"
|
||||
#include "bios.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
while(1)
|
||||
{
|
||||
wait_retrace();
|
||||
intensity(0x7f);
|
||||
print_str_c( 0x10, -0x50, (char*)"HELLO WORLD!" );
|
||||
intensity(0x3f);
|
||||
print_str_c( -0x10, -0x50, (char*)"THIS IS CMOC" );
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
; http://vide.malban.de/help/vectrex-tutorial-ii-starting-with-bios
|
||||
;***************************************************************************
|
||||
; DEFINE SECTION
|
||||
;***************************************************************************
|
||||
Intensity_5F EQU $F2A5 ; BIOS Intensity routine
|
||||
Print_Str_d EQU $F37A ; BIOS print routine
|
||||
Wait_Recal EQU $F192 ; BIOS recalibration
|
||||
music1 EQU $FF8F ; address of a (BIOS ROM)
|
||||
; music
|
||||
; start of vectrex memory with cartridge name...
|
||||
ORG 0
|
||||
;***************************************************************************
|
||||
; HEADER SECTION
|
||||
;***************************************************************************
|
||||
FCC "g GCE 1982"
|
||||
DB $80 ; 'g' is copyright sign
|
||||
DW music1 ; music from the rom
|
||||
DB $F8
|
||||
DB $50
|
||||
DB $20
|
||||
DB -$56 ; height, width, rel y, rel x
|
||||
; (from 0,0)
|
||||
FCC "HELLO WORLD PROG 1"
|
||||
DB $80 ; some game information,
|
||||
; ending with $80
|
||||
DB 0 ; end of game header
|
||||
;***************************************************************************
|
||||
; CODE SECTION
|
||||
;***************************************************************************
|
||||
; here the cartridge program starts off
|
||||
main:
|
||||
JSR Wait_Recal ; Vectrex BIOS recalibration
|
||||
JSR Intensity_5F ; Sets the intensity of the
|
||||
; vector beam to $5f
|
||||
LDU #hello ; address of string
|
||||
LDA #$10 ; Text position relative Y
|
||||
LDB #-$50 ; Text position relative X
|
||||
JSR Print_Str_d ; Vectrex BIOS print routine
|
||||
BRA main ; and repeat forever
|
||||
;***************************************************************************
|
||||
; DATA SECTION
|
||||
;***************************************************************************
|
||||
hello:
|
||||
FCC "HELLO WORLD" ; only capital letters
|
||||
DB $80 ; $80 is end of string
|
||||
;***************************************************************************
|
||||
END main
|
||||
;***************************************************************************
|
|
@ -589,9 +589,6 @@ export abstract class Base6809Platform extends BaseZ80Platform {
|
|||
return cpu;
|
||||
}
|
||||
|
||||
getPC() { return this._cpu.PC; }
|
||||
getSP() { return this._cpu.SP; }
|
||||
|
||||
cpuStateToLongString(c:CpuState) {
|
||||
return cpuStateToLongString_6809(c);
|
||||
}
|
||||
|
|
|
@ -2371,6 +2371,7 @@ return {
|
|||
reset();
|
||||
},
|
||||
getPC: function() { return PC; },
|
||||
getSP: function() { return rS; },
|
||||
saveState: function() {
|
||||
return {
|
||||
PC:PC,
|
||||
|
|
|
@ -349,6 +349,8 @@ export const Keys = {
|
|||
B: {c: 16, n: "Shift", plyr:0, button:1},
|
||||
GP_A: {c: 88, n: "X", plyr:0, button:0},
|
||||
GP_B: {c: 90, n: "Z", plyr:0, button:1},
|
||||
GP_C: {c: 83, n: "S", plyr:0, button:2},
|
||||
GP_D: {c: 65, n: "A", plyr:0, button:3},
|
||||
SELECT: {c: 220, n: "\\", plyr:0, button:8},
|
||||
START: {c: 13, n: "Enter", plyr:0, button:9},
|
||||
// gamepad and keyboard (player 1)
|
||||
|
|
|
@ -3,6 +3,8 @@ import { Platform, BaseZ80Platform, Base6502Platform, Base6809Platform } from ".
|
|||
import { PLATFORMS, newAddressDecoder, padBytes, noise, setKeyboardFromMap, AnimationTimer, VectorVideo, Keys, makeKeycodeMap } from "../common/emu";
|
||||
import { hex, lzgmini, stringToByteArray, safe_extend } from "../common/util";
|
||||
import { MasterAudio, AY38910_Audio } from "../common/audio";
|
||||
import { ProbeRecorder } from "../common/recorder";
|
||||
import { NullProbe, Probeable, ProbeAll } from "../common/devices";
|
||||
|
||||
// emulator from https://github.com/raz0red/jsvecx
|
||||
// https://roadsidethoughts.com/vectrex/vectrex-memory-map.htm
|
||||
|
@ -14,19 +16,19 @@ import { MasterAudio, AY38910_Audio } from "../common/audio";
|
|||
|
||||
var VECTREX_PRESETS = [
|
||||
{ id: 'hello.xasm', name: 'Hello World (ASM)' },
|
||||
{ id: 'hello.c', name: 'Hello World (CMOC)' },
|
||||
]
|
||||
|
||||
// TODO: player 2
|
||||
var VECTREX_KEYCODE_MAP = makeKeycodeMap([
|
||||
[Keys.B, 3, 0xff],
|
||||
[Keys.A, 4, 0xff],
|
||||
[Keys.SELECT, 8, 0xff],
|
||||
[Keys.VK_6, 9, 0xff],
|
||||
[Keys.VK_7, 10, 0xff],
|
||||
[Keys.START, 11, 0xff],
|
||||
[Keys.P2_START, 12, 0xff],
|
||||
[Keys.UP, 13, 0xff],
|
||||
[Keys.RIGHT, 14, 0xff],
|
||||
[Keys.LEFT, 15, 0xff],
|
||||
[Keys.LEFT, 0, 0x01],
|
||||
[Keys.RIGHT, 0, 0x02],
|
||||
[Keys.DOWN, 0, 0x04],
|
||||
[Keys.UP, 0, 0x08],
|
||||
[Keys.GP_A, 2, 0x01],
|
||||
[Keys.GP_B, 2, 0x02],
|
||||
[Keys.GP_C, 2, 0x04],
|
||||
[Keys.GP_D, 2, 0x08],
|
||||
]);
|
||||
|
||||
//
|
||||
|
@ -137,7 +139,7 @@ class VIA6522 {
|
|||
/* continuous interrupt mode */
|
||||
this.ifr |= 0x40;
|
||||
this.int_update();
|
||||
this.t1pb7 = 0x80 - this.t1pb7;
|
||||
this.t1pb7 ^= 0x80;
|
||||
/* reload counter */
|
||||
this.t1c = (this.t1lh << 8) | this.t1ll;
|
||||
}
|
||||
|
@ -284,6 +286,7 @@ class VIA6522 {
|
|||
if ((this.orb & 0x18) == 0x08) {
|
||||
/* the snd chip is driving port a */
|
||||
data = this.vectrex.psg.readData();
|
||||
//console.log(this.vectrex.psg.currentRegister(), data);
|
||||
}
|
||||
else {
|
||||
data = this.ora;
|
||||
|
@ -484,10 +487,14 @@ const Globals =
|
|||
VECTREX_COLORS: 128, /* number of possible colors ... grayscale */
|
||||
ALG_MAX_X: 33000,
|
||||
ALG_MAX_Y: 41000,
|
||||
VECTREX_PDECAY: 30, /* phosphor decay rate */
|
||||
VECTOR_HASH: 65521,
|
||||
SCREEN_X_DEFAULT: 330,
|
||||
SCREEN_Y_DEFAULT: 410
|
||||
//VECTREX_PDECAY: 30, /* phosphor decay rate */
|
||||
//VECTOR_HASH: 65521,
|
||||
SCREEN_X_DEFAULT: 900,
|
||||
SCREEN_Y_DEFAULT: 1100,
|
||||
BOUNDS_MIN_X: 0,
|
||||
BOUNDS_MAX_X: 30000,
|
||||
BOUNDS_MIN_Y: 41000,
|
||||
BOUNDS_MAX_Y: 0,
|
||||
};
|
||||
|
||||
class VectrexAnalog {
|
||||
|
@ -616,14 +623,14 @@ class VectrexAnalog {
|
|||
var sig_ramp = 0;
|
||||
var sig_blank = 0;
|
||||
|
||||
if ((via.acr & 0x10) == 0x10) {
|
||||
if (via.acr & 0x10) {
|
||||
sig_blank = via.cb2s;
|
||||
}
|
||||
else {
|
||||
sig_blank = via.cb2h;
|
||||
}
|
||||
|
||||
if (via.ca2) // pulse mode
|
||||
if (via.ca2 == 0)
|
||||
{
|
||||
/* need to force the current point to the 'orgin' so just
|
||||
* calculate distance to origin and use that as dx,dy.
|
||||
|
@ -648,6 +655,7 @@ class VectrexAnalog {
|
|||
sig_dy = 0;
|
||||
}
|
||||
}
|
||||
//if (sig_dx || sig_dy) console.log(via.ca2, this.curr_x, this.curr_y, this.dx, this.dy, sig_dx, sig_dy, sig_ramp, sig_blank);
|
||||
|
||||
if (!this.vectoring) {
|
||||
if (sig_blank == 1 &&
|
||||
|
@ -724,6 +732,11 @@ class VectrexAnalog {
|
|||
addline(x0, y0, x1, y1, color) {
|
||||
// TODO
|
||||
//console.log(x0, y0, x1, y1, color);
|
||||
x0 = (x0 - Globals.BOUNDS_MIN_X) / (Globals.BOUNDS_MAX_X - Globals.BOUNDS_MIN_X) * Globals.SCREEN_X_DEFAULT;
|
||||
x1 = (x1 - Globals.BOUNDS_MIN_X) / (Globals.BOUNDS_MAX_X - Globals.BOUNDS_MIN_X) * Globals.SCREEN_X_DEFAULT;
|
||||
y0 = (y0 - Globals.BOUNDS_MIN_Y) / (Globals.BOUNDS_MAX_Y - Globals.BOUNDS_MIN_Y) * Globals.SCREEN_Y_DEFAULT;
|
||||
y1 = (y1 - Globals.BOUNDS_MIN_Y) / (Globals.BOUNDS_MAX_Y - Globals.BOUNDS_MIN_Y) * Globals.SCREEN_Y_DEFAULT;
|
||||
this.vectrex.video.drawLine(x0, y0, x1, y1, color, 7);
|
||||
}
|
||||
|
||||
saveState() {
|
||||
|
@ -772,9 +785,8 @@ class VectrexPlatform extends Base6809Platform {
|
|||
this.alg = new VectrexAnalog(this);
|
||||
this.bios = padBytes(new lzgmini().decode(stringToByteArray(atob(VECTREX_FASTROM_LZG))), 0x2000);
|
||||
this.ram = new Uint8Array(0x400);
|
||||
this.inputs = new Uint8Array(16);
|
||||
this.bus = {
|
||||
|
||||
this.inputs = new Uint8Array(4);
|
||||
var mbus = {
|
||||
read: newAddressDecoder([
|
||||
[0x0000, 0x7fff, 0, (a) => { return this.rom && this.rom[a]; }],
|
||||
[0xc800, 0xcfff, 0x3ff, (a) => { return this.ram[a]; }],
|
||||
|
@ -787,26 +799,43 @@ class VectrexPlatform extends Base6809Platform {
|
|||
[0xd000, 0xd7ff, 0x3ff, (a, v) => { this.via.write(a & 0xf, v); }],
|
||||
[0xd800, 0xdfff, 0x3ff, (a, v) => { this.ram[a] = v; this.via.write(a & 0xf, v); }],
|
||||
])
|
||||
|
||||
};
|
||||
this.bus = {
|
||||
read: (a) => { var v = mbus.read(a); this.probe.logRead(a,v); return v; },
|
||||
write: (a,v) => { this.probe.logWrite(a,v); mbus.write(a,v); }
|
||||
};
|
||||
this._cpu = this.newCPU(this.bus);
|
||||
// create video/audio
|
||||
this.video = new VectorVideo(this.mainElement, 660, 820);
|
||||
this.video = new VectorVideo(this.mainElement, Globals.SCREEN_X_DEFAULT, Globals.SCREEN_Y_DEFAULT);
|
||||
this.video.persistenceAlpha = 0.2;
|
||||
this.audio = new MasterAudio();
|
||||
this.psg = new AY38910_Audio(this.audio);
|
||||
this.video.create();
|
||||
this.timer = new AnimationTimer(60, this.nextFrame.bind(this));
|
||||
setKeyboardFromMap(this.video, this.inputs, VECTREX_KEYCODE_MAP);
|
||||
setKeyboardFromMap(this.video, this.inputs, VECTREX_KEYCODE_MAP); // true = always send function);
|
||||
}
|
||||
|
||||
advance() {
|
||||
updateControls() {
|
||||
// joystick (analog simulation)
|
||||
this.alg.jch0 = (this.inputs[0] & 0x1) ? 0x00 : (this.inputs[0] & 0x2) ? 0xff : 0x80;
|
||||
this.alg.jch1 = (this.inputs[0] & 0x4) ? 0x00 : (this.inputs[0] & 0x8) ? 0xff : 0x80;
|
||||
// buttons (digital)
|
||||
this.psg.psg.register[14] = ~this.inputs[2];
|
||||
}
|
||||
|
||||
advance(novideo:boolean) {
|
||||
if (!novideo) this.video.clear();
|
||||
this.updateControls();
|
||||
this.probe.logNewFrame();
|
||||
var cycles = 1500000 / 60;
|
||||
while (cycles > 0) {
|
||||
this.probe.logExecute(this.getPC(), this.getSP());
|
||||
if (this.via.ifr & 0x80) {
|
||||
this._cpu.interrupt();
|
||||
}
|
||||
var n = this.runCPU(this._cpu, 1);
|
||||
if (n == 0) n = 1; // TODO?
|
||||
this.probe.logClocks(n);
|
||||
cycles -= n;
|
||||
for (; n > 0; n--) {
|
||||
this.via.step0();
|
||||
|
@ -919,6 +948,22 @@ class VectrexPlatform extends Base6809Platform {
|
|||
default: return super.getDebugInfo(category, state);
|
||||
}
|
||||
}
|
||||
|
||||
// probing
|
||||
nullProbe = new NullProbe();
|
||||
probe : ProbeAll = this.nullProbe;
|
||||
|
||||
startProbing?() : ProbeRecorder {
|
||||
var rec = new ProbeRecorder(this);
|
||||
this.connectProbe(rec);
|
||||
return rec;
|
||||
}
|
||||
stopProbing?() : void {
|
||||
this.connectProbe(null);
|
||||
}
|
||||
connectProbe(probe:ProbeAll) {
|
||||
this.probe = probe || this.nullProbe;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
/* assert.h - Assert macro for CMOC
|
||||
|
||||
By Pierre Sarrazin <http://sarrazip.com/>.
|
||||
This file is in the public domain.
|
||||
*/
|
||||
|
||||
#ifndef _ASSERT_H
|
||||
#define _ASSERT_H
|
||||
|
||||
#include "cmoc.h"
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define assert(cond)
|
||||
#else
|
||||
#define assert(cond) do { if (!(cond)) { \
|
||||
printf("***ASSERT FAILED: %s:%u: %s\n", __FILE__, __LINE__, #cond); \
|
||||
for (;;); } } while (0)
|
||||
#endif
|
||||
|
||||
#endif /* _ASSERT_H */
|
|
@ -0,0 +1,203 @@
|
|||
#ifndef __vectrex_bios_h__
|
||||
#define __vectrex_bios_h__
|
||||
|
||||
#include "vectrex.h"
|
||||
|
||||
// Expose Vectrex music addreses
|
||||
#define vx_music_1 (char*) 0xFD0D
|
||||
#define vx_music_2 (char*) 0xFD1D
|
||||
#define vx_music_3 (char*) 0xFD81
|
||||
#define vx_music_4 (char*) 0xFDD3
|
||||
#define vx_music_5 (char*) 0xFE38
|
||||
#define vx_music_6 (char*) 0xFE76
|
||||
#define vx_music_7 (char*) 0xFEC6
|
||||
#define vx_music_8 (char*) 0xFEF8
|
||||
#define vx_music_9 (char*) 0xFF26
|
||||
#define vx_music_10 (char*) 0xFF44
|
||||
#define vx_music_11 (char*) 0xFF62
|
||||
#define vx_music_12 (char*) 0xFF7A
|
||||
#define vx_music_13 (char*) 0xFF8F
|
||||
|
||||
#define JOY1_BTN1 0
|
||||
#define JOY1_BTN2 1
|
||||
#define JOY1_BTN3 2
|
||||
#define JOY1_BTN4 3
|
||||
|
||||
#define JOY2_BTN1 4
|
||||
#define JOY2_BTN2 5
|
||||
#define JOY2_BTN3 6
|
||||
#define JOY2_BTN4 7
|
||||
|
||||
#define JOY1_BTN1_MASK (1<<JOY1_BTN1)
|
||||
#define JOY1_BTN2_MASK (1<<JOY1_BTN2)
|
||||
#define JOY1_BTN3_MASK (1<<JOY1_BTN3)
|
||||
#define JOY1_BTN4_MASK (1<<JOY1_BTN4)
|
||||
|
||||
#define JOY2_BTN1_MASK (1<<JOY2_BTN1)
|
||||
#define JOY2_BTN2_MASK (1<<JOY2_BTN2)
|
||||
#define JOY2_BTN3_MASK (1<<JOY2_BTN3)
|
||||
#define JOY2_BTN4_MASK (1<<JOY2_BTN4)
|
||||
|
||||
#define JOY_UP 0
|
||||
#define JOY_DOWN 1
|
||||
#define JOY_LEFT 2
|
||||
#define JOY_RIGHT 3
|
||||
|
||||
#define JOY_UP_MASK (1<<JOY_UP)
|
||||
#define JOY_DOWN_MASK (1<<JOY_DOWN)
|
||||
#define JOY_LEFT_MASK (1<<JOY_RIGHT)
|
||||
#define JOY_RIGHT_MASK (1<<JOY_LEFT)
|
||||
|
||||
#define JOY_UP_MASK_ASM 1
|
||||
#define JOY_DOWN_MASK_ASM 2
|
||||
#define JOY_LEFT_MASK_ASM 4
|
||||
#define JOY_RIGHT_MASK_ASM 8
|
||||
|
||||
|
||||
// Read the joystick buttons.
|
||||
// uint8_t buttons = read_buttons();
|
||||
// if (buttons & JOY1_BTN1_MASK) {
|
||||
// do stuff when joystick one button one is pressed
|
||||
// ...
|
||||
// }
|
||||
uint8_t read_buttons();
|
||||
|
||||
|
||||
// Read the joystick positions. This is an expensive call, as the Vectrex makes use of measring time over
|
||||
// an RC network to do AD conversion for the joystick positions. For the moment, only digital positions are
|
||||
// measured with this routine.
|
||||
// You can use this routine as follows:
|
||||
// uint8_t joystick = read_joystick(1);
|
||||
// if ( joystick & JOY_UP_MASK) {
|
||||
// do joystick up stuff
|
||||
// ...
|
||||
// }
|
||||
uint8_t read_joystick(uint8_t joystick);
|
||||
|
||||
|
||||
// This call waits for timer two to timeout and restarts it, it then recalibrates the vector generators to
|
||||
// coordinate (0, 0) it also calls the BIOS routine behind reset_beam in the process.
|
||||
void wait_retrace();
|
||||
|
||||
|
||||
// Set the beam intensity. The value 0x7f is often used and is rather bright.
|
||||
void intensity(uint8_t i);
|
||||
|
||||
|
||||
// Reset the beam to the middle of the screen.
|
||||
// This is necassary from time to time as there is drift on the Vectrex integrators.
|
||||
// Caling this makes coordinate (0,0) the middle of the screen.
|
||||
void reset_beam();
|
||||
|
||||
|
||||
// Set the text width and height. Negative values are needed for the y coordinate. Otherwise text will be upside down.
|
||||
void set_text_size(int8_t height, int8_t width);
|
||||
|
||||
|
||||
// Print a string to the screen. The string must end with 0x80
|
||||
void print_str(int8_t y, int8_t x, char* string);
|
||||
|
||||
|
||||
// Print a C string (ends with 0).
|
||||
void print_str_c(int8_t y, int8_t x, char* string);
|
||||
|
||||
|
||||
// Draw one dot at the relative y and x coordinate from the last beam position.
|
||||
void dot(int8_t y, int8_t x);
|
||||
|
||||
|
||||
// Draw a list of dots to the screen. Each dot is positioned relative to the previous one.
|
||||
void dots(uint8_t nr_dots, int8_t* list);
|
||||
|
||||
|
||||
// Set the scale. With this you can zoom in and out objects.
|
||||
void set_scale(int8_t scale);
|
||||
|
||||
|
||||
// Move the dot to a specific position.
|
||||
void move(uint8_t y, uint8_t x);
|
||||
|
||||
|
||||
// Draw a line.
|
||||
void line(int8_t y, int8_t x);
|
||||
|
||||
|
||||
// Draw lines.
|
||||
// The list of lines can live in ROM.
|
||||
// You provide the list of lines as follows:
|
||||
// int8_t list[n] = {
|
||||
// y1, x1,
|
||||
// y2, x2,
|
||||
// ...
|
||||
// xn, yn
|
||||
// };
|
||||
// Make note that these are not absolute, but relative coordinates.
|
||||
void lines(uint8_t nr_lines, int8_t* list);
|
||||
|
||||
|
||||
// Draw patterned lines, where the pattern is provided as the first parameter, for the rest
|
||||
// this function behaves the same as the lines fucntion.
|
||||
void pattern_lines(uint8_t pattern, uint8_t nr_lines, int8_t *list);
|
||||
|
||||
|
||||
// Rotate a list of points. The points array can be in ROM, but the out_points need to be in RAM.
|
||||
// The list of points is given as follows:
|
||||
// int8_t points[n] = {
|
||||
// y1, x1,
|
||||
// y2, x2,
|
||||
// ...
|
||||
// yn, xn
|
||||
// };
|
||||
void rotate(int8_t angle, uint8_t nr_points, int8_t* points, int8_t* out_points);
|
||||
|
||||
|
||||
// Set the music flag, which should be set to 1 to start playing music using the BIOS routine.
|
||||
// The music flag has 3 states: 0, 1 and 0x80.
|
||||
// State 0 means no music is playing. A 1 means we are about to start the music.
|
||||
// And 0x80 means the music is currently playing.
|
||||
void music_set_flag(uint8_t flag);
|
||||
|
||||
|
||||
// Get the music flag, which can have the states documented with the get method.
|
||||
// If you want to repeat the music, you should check this flag and if it is 0 set to 1 again.
|
||||
uint8_t music_get_flag();
|
||||
|
||||
|
||||
// Point the BIOS play routine to a piece of music.
|
||||
// Info about the format: http://www.playvectrex.com/designit/chrissalo/soundplaying.htm
|
||||
void music_check(unsigned char* music);
|
||||
|
||||
|
||||
// Call this in the main loop to play the music.
|
||||
void music_play();
|
||||
|
||||
|
||||
// The Vectrex uses three bytes for the random seed.
|
||||
void random_seed(uint8_t seed1, uint8_t seed2, uint8_t seed3);
|
||||
|
||||
|
||||
// Produce a random value using the BIOS Random function.
|
||||
int8_t random();
|
||||
|
||||
|
||||
// What follows here are some low level BIOS fucntions that are not really necessary to call from your
|
||||
// C program, but the are here for completeness.
|
||||
void cold_start();
|
||||
|
||||
|
||||
void warm_start();
|
||||
|
||||
|
||||
void init_via();
|
||||
|
||||
|
||||
void init_os_ram();
|
||||
|
||||
|
||||
void init_os();
|
||||
|
||||
|
||||
void set_refresh(uint16_t value);
|
||||
|
||||
|
||||
#endif // __vectrex_bios_h__
|
|
@ -0,0 +1,345 @@
|
|||
// cmoc.h - CMOC's standard library functions.
|
||||
//
|
||||
// By Pierre Sarrazin <http://sarrazip.com/>.
|
||||
// This file is in the public domain.
|
||||
//
|
||||
// Functions not documented here should be assumed to behave as in C.
|
||||
|
||||
#ifndef _H_CMOC
|
||||
#define _H_CMOC
|
||||
|
||||
#ifndef __GNUC__
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL ((void *) 0)
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef _CMOC_CONST_
|
||||
#define _CMOC_CONST_ const
|
||||
#endif
|
||||
|
||||
|
||||
// Gives the offset in bytes of the specified 'member' in the struct
|
||||
// or union named 'Type'.
|
||||
//
|
||||
#define offsetof(Type, member) ((unsigned) &((Type *) 0)->member)
|
||||
|
||||
typedef unsigned size_t;
|
||||
|
||||
|
||||
#ifndef VECTREX
|
||||
// Supports %u, %d, %x, %X, %p, %s, %c and %%. Specifying a field width is
|
||||
// allowed, but a left justification is only supported for strings, i.e.,
|
||||
// %-15s will work, but %-6u will not. Zero padding for a number is supported
|
||||
// (e.g., %04x).
|
||||
//
|
||||
int printf(_CMOC_CONST_ char *format, ...);
|
||||
#endif
|
||||
|
||||
|
||||
// Writes to 'dest'. Not thread-safe. Does not check for buffer overflow.
|
||||
int sprintf(char *dest, _CMOC_CONST_ char *format, ...);
|
||||
|
||||
|
||||
#ifndef VECTREX
|
||||
// Writes the first 'n' characters designated by 's', regardless of any
|
||||
// null characters encountered among them.
|
||||
//
|
||||
void putstr(_CMOC_CONST_ char *s, size_t n);
|
||||
|
||||
void putchar(int c);
|
||||
#endif
|
||||
|
||||
int strcmp(_CMOC_CONST_ char *s1, _CMOC_CONST_ char *s2);
|
||||
int stricmp(_CMOC_CONST_ char *s1, _CMOC_CONST_ char *s2);
|
||||
int memcmp(_CMOC_CONST_ void *s1, _CMOC_CONST_ void *s2, size_t n);
|
||||
int memicmp(_CMOC_CONST_ void *s1, _CMOC_CONST_ void *s2, size_t n);
|
||||
void *memcpy(void *dest, _CMOC_CONST_ void *src, size_t n);
|
||||
void *memset(void *s, int c, size_t n);
|
||||
size_t strlen(_CMOC_CONST_ char *s);
|
||||
char *strcpy(char *dest, _CMOC_CONST_ char *src);
|
||||
char *strcat(char *dest, _CMOC_CONST_ char *src);
|
||||
char *strncpy(char *dest, _CMOC_CONST_ char *src, size_t n);
|
||||
char *strchr(_CMOC_CONST_ char *s, int c);
|
||||
char *strstr(const char *haystack, const char *needle);
|
||||
char *strlwr(char *s);
|
||||
char *strupr(char *s);
|
||||
|
||||
|
||||
// Converts an ASCII unsigned decimal string into an unsigned word.
|
||||
//
|
||||
unsigned atoui(_CMOC_CONST_ char *s);
|
||||
|
||||
|
||||
// Converts an ASCII signed decimal string into a signed word.
|
||||
//
|
||||
int atoi(_CMOC_CONST_ char *s);
|
||||
|
||||
|
||||
// Converts an integer to a NUL-terminated ASCII signed decimal string.
|
||||
// Returns 'str'.
|
||||
// The caller must be careful to pass an array of sufficient size,
|
||||
// including room for the terminating '\0'.
|
||||
//
|
||||
char *itoa10(int value, char *str);
|
||||
|
||||
|
||||
// Converts an integer to a NUL-terminated ASCII signed decimal string.
|
||||
// N.B.: 'base' must be 10. No other base is supported by this implementation.
|
||||
// Returns 'str'.
|
||||
// The caller must be careful to pass an array of sufficient size,
|
||||
// including room for the terminating '\0'.
|
||||
//
|
||||
#define itoa(value, str, base) (itoa10((value), (str)))
|
||||
|
||||
|
||||
// Like itoa10(), but 'value' can be in the range 32768..65535.
|
||||
//
|
||||
char *utoa10(unsigned value, char *str);
|
||||
|
||||
|
||||
// Like itoa(), but 'value' can be in the range 32768..65535.
|
||||
// N.B.: 'base' must be 10. No other base is supported by this implementation.
|
||||
//
|
||||
#define utoa(value, str, base) (utoa10((value), (str)))
|
||||
|
||||
|
||||
// Like itoa10(), but 'value' can be 32 bits.
|
||||
//
|
||||
char *ltoa10(long value, char *str);
|
||||
|
||||
|
||||
// Like itoa(), but 'value' can be 32 bits.
|
||||
// N.B.: 'base' must be 10. No other base is supported by this implementation.
|
||||
//
|
||||
#define ltoa(value, str, base) (ltoa10((value), (str)))
|
||||
|
||||
|
||||
// Like utoa10(), but 'value' can be 32 bits.
|
||||
//
|
||||
char *ultoa10(unsigned long value, char *str);
|
||||
|
||||
|
||||
// Like utoa(), but 'value' can be 32 bits.
|
||||
// N.B.: 'base' must be 10. No other base is supported by this implementation.
|
||||
//
|
||||
#define ultoa(value, str, base) (ultoa10((value), (str)))
|
||||
|
||||
|
||||
// Double-word to ASCII.
|
||||
// Converts the unsigned 32-bit integer formed by hi * 65536 + lo into
|
||||
// an ASCII decimal representation that gets written to 'out'.
|
||||
// 'out' must point to at least 11 bytes. The string written there will
|
||||
// be terminated by a null character.
|
||||
// Returns the address of the first non-'0' character in the 11-byte
|
||||
// buffer, or to "0" if hi and lo are both zero.
|
||||
// Example: char s[11]; char *p = dwtoa(s, 1, 2);
|
||||
// s will get the string "
|
||||
// NOTE: This operation can also be done with the 'long' type and by
|
||||
// calling sprintf() with the "%lu" or "%ld" placeholders.
|
||||
//
|
||||
char *dwtoa(char *out, unsigned hi, unsigned lo);
|
||||
|
||||
|
||||
// Returns the integer part of the square root of n.
|
||||
//
|
||||
unsigned char sqrt16(unsigned n);
|
||||
|
||||
|
||||
// Returns the quotient and remainder of a 16-bit unsigned division
|
||||
// in a single operation.
|
||||
//
|
||||
void divmod16(unsigned dividend, unsigned divisor,
|
||||
unsigned *quotient, unsigned *remainder);
|
||||
|
||||
|
||||
// Returns the quotient and remainder of an 8-bit unsigned division
|
||||
// in a single operation.
|
||||
//
|
||||
void divmod8(unsigned char dividend, unsigned char divisor,
|
||||
unsigned char *quotient, unsigned char *remainder);
|
||||
|
||||
|
||||
// Divides an unsigned 32-bit integer by an unsigned 8-bit integer.
|
||||
// The two words designated by 'dividendInQuotientOut' are the input dividend.
|
||||
// The 32-bit quotient is left in those two words.
|
||||
//
|
||||
void divdwb(unsigned dividendInQuotientOut[2], unsigned char divisor);
|
||||
|
||||
|
||||
// Previous name of divdwb().
|
||||
//
|
||||
#define div328 divdwb
|
||||
|
||||
|
||||
// Divides an unsigned 32-bit integer by an unsigned 16-bit integer.
|
||||
// The two words designated by 'dividendInQuotientOut' are the input dividend.
|
||||
// The 32-bit quotient is left in those two words.
|
||||
//
|
||||
void divdww(unsigned dividendInQuotientOut[2], unsigned divisor);
|
||||
|
||||
|
||||
// Multiply a word by a byte.
|
||||
// Stores the high word of the product in *hi and returns the low word.
|
||||
//
|
||||
unsigned mulwb(unsigned char *hi, unsigned wordFactor, unsigned char byteFactor);
|
||||
|
||||
|
||||
// Similar to mulwb().
|
||||
unsigned mulww(unsigned *hi, unsigned factor0, unsigned factor1);
|
||||
|
||||
|
||||
// Stores 0 in twoWords[0], twoWords[1].
|
||||
//
|
||||
void zerodw(unsigned *twoWords);
|
||||
|
||||
|
||||
// Adds the 16-bit integer 'term' to the 32-bit integer designated by
|
||||
// twoWords[0] and twoWords[1].
|
||||
//
|
||||
void adddww(unsigned *twoWords, unsigned term);
|
||||
|
||||
|
||||
// Subtracts the 16-bit integer 'term' from the 32-bit integer designated by
|
||||
// twoWords[0] and twoWords[1].
|
||||
//
|
||||
void subdww(unsigned *twoWords, unsigned term);
|
||||
|
||||
|
||||
// Returns 0 if the 32-bit unsigned word composed of left[0] and left[1]
|
||||
// (where left[0] is the high word) is equal to 'right';
|
||||
// returns +1 if left > right; -1 if left < right.
|
||||
//
|
||||
char cmpdww(unsigned left[2], unsigned right);
|
||||
|
||||
|
||||
#ifdef _COCO_BASIC_
|
||||
|
||||
// Converts an ASCII decimal floating point number to a float.
|
||||
// The string is allowed to contain a suffix (e.g., "1.2E6XYZ");
|
||||
// endptr: Receives the address where the parsing stopped.
|
||||
// Caution: Passing a string whose value does not fit in a float
|
||||
// may have undefined behavior.
|
||||
// An 'E' used in exponential notation must be in upper-case.
|
||||
//
|
||||
float strtof(_CMOC_CONST_ char *nptr, char **endptr);
|
||||
|
||||
|
||||
// Like strtof(), but does not return the end pointer.
|
||||
//
|
||||
float atoff(_CMOC_CONST_ char *nptr);
|
||||
|
||||
|
||||
// Writes an ASCII decimal representation of 'f' in the buffer
|
||||
// at 'out' which must contain at least 38 bytes.
|
||||
// Returns 'out' upon success, or null upon failure.
|
||||
//
|
||||
char *ftoa(char out[38], float f);
|
||||
|
||||
#endif /* _COCO_BASIC_ */
|
||||
|
||||
|
||||
// CAUTION: base is ignored, only base 10 is supported.
|
||||
//
|
||||
unsigned long strtoul(_CMOC_CONST_ char *nptr, char **endptr, int base);
|
||||
|
||||
unsigned long atoul(_CMOC_CONST_ char *nptr);
|
||||
|
||||
// CAUTION: base is ignored, only base 10 is supported.
|
||||
//
|
||||
long strtol(_CMOC_CONST_ char *nptr, char **endptr, int base);
|
||||
|
||||
long atol(_CMOC_CONST_ char *nptr);
|
||||
|
||||
int tolower(int c);
|
||||
int toupper(int c);
|
||||
void exit(int status);
|
||||
|
||||
#define RAND_MAX 0x7FFF
|
||||
void srand(unsigned seed);
|
||||
int rand();
|
||||
|
||||
// See the CMOC manual.
|
||||
void *sbrk(size_t increment);
|
||||
size_t sbrkmax();
|
||||
void set_null_ptr_handler(void (*newHandler)(void *));
|
||||
void set_stack_overflow_handler(void (*newHandler)(void *, void *));
|
||||
|
||||
|
||||
// Function pointer type used by setConsoleOutHook().
|
||||
//
|
||||
typedef void (*ConsoleOutHook)();
|
||||
|
||||
|
||||
// Redirect printf() et al. to the function at 'routine', which will
|
||||
// receive each character to be printed in register A.
|
||||
//
|
||||
// That routine MUST preserve registers B, X, Y and U.
|
||||
//
|
||||
// If this function is never called, printf() et al. write to the
|
||||
// system's standard character output routine.
|
||||
//
|
||||
// Returns the original output routine address.
|
||||
// To uninstall the new routine, call this function again with
|
||||
// the original routine address.
|
||||
//
|
||||
ConsoleOutHook setConsoleOutHook(ConsoleOutHook routine);
|
||||
|
||||
|
||||
#ifndef VECTREX
|
||||
|
||||
// Blocks the execution for the specified time in 60ths of a second.
|
||||
//
|
||||
void delay(size_t sixtiethsOfASecond);
|
||||
|
||||
|
||||
// Reads a line from standard input, converts an expected 16-bit decimal
|
||||
// number and returns it. Not thread-safe.
|
||||
//
|
||||
unsigned readword();
|
||||
|
||||
|
||||
// Reads a line from standard input and returns it.
|
||||
// Not thread-safe.
|
||||
// Returns a null pointer if the operation failed (e.g., end of file
|
||||
// encountered).
|
||||
//
|
||||
char *readline();
|
||||
|
||||
#endif /* ndef VECTREX */
|
||||
|
||||
|
||||
// Sorts an array that starts at 'base', that has 'nmemb' elements, whose
|
||||
// elements are 'size' bytes each.
|
||||
// compar: Pointer to a function that receives pointers to two array elements
|
||||
// and that returns -1, 0 or +1 depending on whether the first element
|
||||
// comes before, is equal to, or comes after the second element.
|
||||
// This function is recursive and will thus use stack space.
|
||||
//
|
||||
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(_CMOC_CONST_ void *, _CMOC_CONST_ void *));
|
||||
|
||||
|
||||
// Searches for the value pointed to by 'key' in the array starting at 'base',
|
||||
// that has 'nmemb' elements, whose elements are 'size' bytes each.
|
||||
// compar: Pointer to a function that receives pointers to the targeted key and
|
||||
// to an array element. It must return -1, 0 or +1 depending on whether
|
||||
// the targeted key comes before, is equal to, or comes after the second element.
|
||||
// Returns a pointer to the element of the array that matches the targeted key,
|
||||
// or NULL if none is found.
|
||||
// The time taken by this function is proportional to the logarithm of the array size.
|
||||
// This function is recursive and will thus use stack space.
|
||||
//
|
||||
void *bsearch(const void *key, const void *base, size_t nmemb, size_t size,
|
||||
int (*compar)(_CMOC_CONST_ void *key, _CMOC_CONST_ void *element));
|
||||
|
||||
|
||||
#else
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#endif /* __GNUC__ */
|
||||
|
||||
#endif /* _H_CMOC */
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,22 @@
|
|||
/* stdarg.h - Support for variable argument functions
|
||||
|
||||
By Pierre Sarrazin <http://sarrazip.com/>.
|
||||
This file is in the public domain.
|
||||
*/
|
||||
|
||||
#ifndef _stdarg_h_
|
||||
#define _stdarg_h_
|
||||
|
||||
|
||||
typedef char *va_list;
|
||||
|
||||
|
||||
char *__va_arg(va_list *app, unsigned int sizeInBytes);
|
||||
|
||||
|
||||
#define va_start(ap, lastFixed) do { (ap) = (char *) &(lastFixed) + sizeof(lastFixed); } while (0)
|
||||
#define va_arg(ap, type) (* (type *) __va_arg(&(ap), sizeof(type)))
|
||||
#define va_end(ap) do {} while (0)
|
||||
|
||||
|
||||
#endif /* _stdarg_h_ */
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef __vectrex_stdlib_h__
|
||||
#define __vectrex_stdlib_h__
|
||||
|
||||
/**
|
||||
* Most stdlib functions are supported on the Vectrex, except those that imply having a keyboard and console:
|
||||
* printf, putsr, putchar, readword, readline.
|
||||
* Also delay is not implemented as it does not seem to make much sense to have a delay function on the Vectrex.
|
||||
*/
|
||||
|
||||
#include "vectrex.h"
|
||||
#include "cmoc.h"
|
||||
|
||||
#endif // __vectrex_stdlib_h__
|
|
@ -0,0 +1,27 @@
|
|||
// This file is in the public domain.
|
||||
|
||||
#ifndef __vectrex_h__
|
||||
#define __vectrex_h__
|
||||
|
||||
enum { FALSE, TRUE };
|
||||
|
||||
#ifndef _CMOC_BASIC_TYPES_
|
||||
#define _CMOC_BASIC_TYPES_
|
||||
|
||||
typedef unsigned char byte;
|
||||
typedef signed char sbyte;
|
||||
typedef unsigned int word;
|
||||
typedef signed int sword;
|
||||
typedef unsigned long dword;
|
||||
typedef signed long sdword;
|
||||
|
||||
#endif
|
||||
|
||||
typedef unsigned char uint8_t;
|
||||
typedef signed char int8_t;
|
||||
typedef unsigned int uint16_t;
|
||||
typedef signed int int16_t;
|
||||
typedef unsigned long uint32_t;
|
||||
typedef signed long int32_t;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,17 @@
|
|||
define basesympat s_%s
|
||||
define lensympat l_%s
|
||||
section start load 0
|
||||
section code
|
||||
section constructors_start
|
||||
section constructors
|
||||
section constructors_end
|
||||
section destructors_start
|
||||
section destructors
|
||||
section destructors_end
|
||||
section initgl_start
|
||||
section initgl
|
||||
section initgl_end
|
||||
section rodata
|
||||
section rwdata load c880
|
||||
section bss,bss
|
||||
entry program_start
|
|
@ -253,7 +253,9 @@ var PLATFORM_PARAMS = {
|
|||
data_start: 0xc880,
|
||||
data_size: 0x380,
|
||||
stack_end: 0xcc00,
|
||||
extra_compile_files: ['assert.h','cmoc.h','stdarg.h','vectrex.h','stdlib.h','bios.h'],
|
||||
extra_link_files: ['vectrex.scr', 'libcmoc-crt-vec.a', 'libcmoc-std-vec.a'],
|
||||
extra_compile_args: ['--vectrex'],
|
||||
extra_link_args: ['-svectrex.scr', '-lcmoc-crt-vec', '-lcmoc-std-vec'],
|
||||
},
|
||||
};
|
||||
|
@ -2099,12 +2101,6 @@ function compileCMOC(step:BuildStep) {
|
|||
//logReadFiles:true,
|
||||
print:match_fn,
|
||||
printErr:match_fn,
|
||||
//arguments:args,
|
||||
/*
|
||||
locateFile: (path,prefix) => {
|
||||
return prefix + 'wasm/' + path;
|
||||
}
|
||||
*/
|
||||
});
|
||||
// load source file and preprocess
|
||||
var code = getWorkFileAsString(step.path);
|
||||
|
@ -2117,6 +2113,9 @@ function compileCMOC(step:BuildStep) {
|
|||
populateFiles(step, FS);
|
||||
FS.writeFile(step.path, code);
|
||||
fixParamsWithDefines(step.path, params);
|
||||
if (params.extra_compile_args) {
|
||||
args.unshift.apply(args, params.extra_compile_args);
|
||||
}
|
||||
execMain(step, CMOC, args);
|
||||
if (errors.length)
|
||||
return {errors:errors};
|
||||
|
|
Loading…
Reference in New Issue