mirror of
https://github.com/whscullin/apple2js.git
synced 2024-01-12 14:14:38 +00:00
Typescript conversion of several files, including js/cpu6502 (#38)
* Convert `js/util.js` to Typescript and add tests Besides converting `js/util.js` to Typescript, this change also adds `js/types.ts` that defines common types used in apple2js. Some of these types, like `byte` and `word` are for information only. * Convert `js/base64.js` to Typescript This also adds a new type, `memory`, that is either an array of numbers, or a Uint8Array. * Convert `js/ram.js` to Typescript This change does not convert `RAM` to a class; it just introduces types. * Basic typing of cpu6502 This is a really rough first pass. There are some problems that can't be fixed until this is turned into a real class, but at least all of the function arguments are now typed. This caught a few cases where extra arguments were being passed in. * Convert `js/cpu6502` to a class In theory, idiomatic classes should be better than the previous closure-based classes. However, this conversion shows that the instruction table does not fit well with idiomatic classes as method referenced in the table need to be called with the correct `this` everywhere. This should, at best, be considered a first attempt.
This commit is contained in:
parent
e8cd85f54a
commit
c4df78cf06
@ -1,4 +1,9 @@
|
||||
export function base64_encode (data) {
|
||||
import { byte, memory } from "./types";
|
||||
|
||||
const B64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
||||
|
||||
/** Encode an array of bytes in base64. */
|
||||
export function base64_encode(data: memory) {
|
||||
// Twacked by Will Scullin to handle arrays of "bytes"
|
||||
|
||||
// http://kevin.vanzonneveld.net
|
||||
@ -18,7 +23,7 @@ export function base64_encode (data) {
|
||||
// return atob(data);
|
||||
//}
|
||||
|
||||
var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
||||
|
||||
var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, ac = 0, enc='', tmp_arr = [];
|
||||
|
||||
if (!data) {
|
||||
@ -38,7 +43,7 @@ export function base64_encode (data) {
|
||||
h4 = bits & 0x3f;
|
||||
|
||||
// use hexets to index into b64, and append result to encoded string
|
||||
tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
|
||||
tmp_arr[ac++] = B64.charAt(h1) + B64.charAt(h2) + B64.charAt(h3) + B64.charAt(h4);
|
||||
} while (i < data.length);
|
||||
|
||||
enc = tmp_arr.join('');
|
||||
@ -55,7 +60,12 @@ export function base64_encode (data) {
|
||||
return enc;
|
||||
}
|
||||
|
||||
export function base64_decode(data) {
|
||||
/** Returns undefined if the input is null or undefined. */
|
||||
export function base64_decode(data: null | undefined): undefined;
|
||||
/** Returns an array of bytes from the given base64-encoded string. */
|
||||
export function base64_decode(data: string): memory;
|
||||
/** Returns an array of bytes from the given base64-encoded string. */
|
||||
export function base64_decode(data: string | null | undefined): memory | undefined {
|
||||
// Twacked by Will Scullin to handle arrays of "bytes"
|
||||
|
||||
// http://kevin.vanzonneveld.net
|
||||
@ -78,18 +88,17 @@ export function base64_decode(data) {
|
||||
// return btoa(data);
|
||||
//}
|
||||
|
||||
var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
||||
var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, ac = 0, tmp_arr = [];
|
||||
|
||||
if (!data) {
|
||||
return data;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
do { // unpack four hexets into three octets using index points in b64
|
||||
h1 = b64.indexOf(data.charAt(i++));
|
||||
h2 = b64.indexOf(data.charAt(i++));
|
||||
h3 = b64.indexOf(data.charAt(i++));
|
||||
h4 = b64.indexOf(data.charAt(i++));
|
||||
do { // unpack four hexets into three octets using index points in B64
|
||||
h1 = B64.indexOf(data.charAt(i++));
|
||||
h2 = B64.indexOf(data.charAt(i++));
|
||||
h3 = B64.indexOf(data.charAt(i++));
|
||||
h4 = B64.indexOf(data.charAt(i++));
|
||||
|
||||
bits = h1<<18 | h2<<12 | h3<<6 | h4;
|
||||
|
1633
js/cpu6502.js
1633
js/cpu6502.js
File diff suppressed because it is too large
Load Diff
1759
js/cpu6502.ts
Normal file
1759
js/cpu6502.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -10,30 +10,43 @@
|
||||
*/
|
||||
|
||||
import { base64_decode, base64_encode } from './base64';
|
||||
import { byte, memory } from './types';
|
||||
import { allocMemPages } from './util';
|
||||
|
||||
export default function RAM(sp, ep) {
|
||||
var mem;
|
||||
var start_page = sp;
|
||||
var end_page = ep;
|
||||
export interface State {
|
||||
/** Start of memory region. */
|
||||
start: byte;
|
||||
/** End of memory region. */
|
||||
end: byte;
|
||||
/** Base64-encoded contents. */
|
||||
mem: string;
|
||||
};
|
||||
|
||||
mem = allocMemPages(ep - sp + 1);
|
||||
/**
|
||||
* Represents RAM from the start page `sp` to end page `ep`. The memory
|
||||
* is addressed by `page` and `offset`.
|
||||
*/
|
||||
export default function RAM(sp: byte, ep: byte) {
|
||||
let start_page = sp;
|
||||
let end_page = ep;
|
||||
|
||||
let mem = allocMemPages(ep - sp + 1);
|
||||
|
||||
return {
|
||||
start: function() {
|
||||
start: function () {
|
||||
return start_page;
|
||||
},
|
||||
end: function() {
|
||||
end: function () {
|
||||
return end_page;
|
||||
},
|
||||
read: function(page, off) {
|
||||
return mem[(page - start_page) << 8 | off];
|
||||
read: function (page: byte, offset: byte) {
|
||||
return mem[(page - start_page) << 8 | offset];
|
||||
},
|
||||
write: function(page, off, val) {
|
||||
mem[(page - start_page) << 8 | off] = val;
|
||||
write: function (page: byte, offset: byte, val: byte) {
|
||||
mem[(page - start_page) << 8 | offset] = val;
|
||||
},
|
||||
|
||||
getState: function() {
|
||||
getState: function (): State {
|
||||
return {
|
||||
start: start_page,
|
||||
end: end_page,
|
||||
@ -41,7 +54,7 @@ export default function RAM(sp, ep) {
|
||||
};
|
||||
},
|
||||
|
||||
setState: function(state) {
|
||||
setState: function (state: State) {
|
||||
start_page = state.start;
|
||||
end_page = state.end;
|
||||
mem = base64_decode(state.mem);
|
@ -1,4 +1,4 @@
|
||||
var SYMBOLS = {
|
||||
SYMBOLS = {
|
||||
/*
|
||||
0x00: 'GOWARM',
|
||||
0x03: 'GOSTROUT',
|
||||
|
27
js/types.ts
Normal file
27
js/types.ts
Normal file
@ -0,0 +1,27 @@
|
||||
|
||||
/** A byte (0..255). This is not enforced by the compiler. */
|
||||
export type byte = number;
|
||||
|
||||
/** A word (0..65535). This is not enforced by the compiler. */
|
||||
export type word = number;
|
||||
|
||||
/** A region of memory. */
|
||||
export type memory = number[] | Uint8Array;
|
||||
|
||||
export type DiskFormat = '2mg' | 'd13' | 'do' | 'dsk' | 'hdv' | 'po' | 'nib' | 'woz';
|
||||
|
||||
export interface Drive {
|
||||
format: DiskFormat,
|
||||
volume: number,
|
||||
tracks: Array<byte[] | Uint8Array>,
|
||||
trackMap: unknown,
|
||||
};
|
||||
|
||||
export interface DiskIIDrive extends Drive {
|
||||
rawTracks: unknown,
|
||||
track: number,
|
||||
head: number,
|
||||
phase: number,
|
||||
readOnly: boolean,
|
||||
dirty: boolean,
|
||||
};
|
@ -9,27 +9,39 @@
|
||||
* implied warranty.
|
||||
*/
|
||||
|
||||
import { byte, memory, word } from "./types";
|
||||
|
||||
/*eslint no-console: 0*/
|
||||
|
||||
var hex_digits = '0123456789ABCDEF';
|
||||
var bin_digits = '01';
|
||||
const hex_digits = '0123456789ABCDEF';
|
||||
const bin_digits = '01';
|
||||
|
||||
export function allocMem(size) {
|
||||
function garbage() {
|
||||
return (Math.random() * 0x100) & 0xff;
|
||||
}
|
||||
var result;
|
||||
/** Returns a random byte. */
|
||||
function garbage(): byte {
|
||||
return (Math.random() * 0x100) & 0xff;
|
||||
}
|
||||
|
||||
export const testables = {
|
||||
garbage
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns an array or Uint8Array of `size` bytes filled as if the computer
|
||||
* was just powered on.
|
||||
*/
|
||||
export function allocMem(size: number) {
|
||||
let result: number[] | Uint8Array;
|
||||
if (window.Uint8Array) {
|
||||
result = new Uint8Array(size);
|
||||
} else {
|
||||
result = new Array(size);
|
||||
}
|
||||
var idx;
|
||||
for (idx = 0; idx < size; idx++) {
|
||||
|
||||
for (let idx = 0; idx < size; idx++) {
|
||||
result[idx] = (idx & 0x02) ? 0x00 : 0xff;
|
||||
}
|
||||
// Borrowed from AppleWin (https://github.com/AppleWin/AppleWin)
|
||||
for(idx = 0; idx < size; idx += 0x200 ) {
|
||||
for (let idx = 0; idx < size; idx += 0x200) {
|
||||
result[idx + 0x28] = garbage();
|
||||
result[idx + 0x29] = garbage();
|
||||
result[idx + 0x68] = garbage();
|
||||
@ -38,23 +50,33 @@ export function allocMem(size) {
|
||||
return result;
|
||||
}
|
||||
|
||||
export function allocMemPages(pages) {
|
||||
/** Returns an array or Uint8Array of 256 * `pages` bytes. */
|
||||
export function allocMemPages(pages: number): memory {
|
||||
return allocMem(pages << 8);
|
||||
}
|
||||
|
||||
export function bytify(ary) {
|
||||
var result = ary;
|
||||
/** Returns a new Uint8Array for the input array. */
|
||||
export function bytify(ary: number[]): memory {
|
||||
let result: number[] | Uint8Array = ary;
|
||||
if (window.Uint8Array) {
|
||||
result = new Uint8Array(ary);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Writes to the console. */
|
||||
export function debug(...args: any[]): void;
|
||||
export function debug() {
|
||||
console.log.apply(console, arguments);
|
||||
}
|
||||
|
||||
export function toHex(v, n) {
|
||||
/**
|
||||
* Returns a string of hex digits (all caps).
|
||||
* @param v the value to encode
|
||||
* @param n the number of nibbles. If `n` is missing, it is guessed from the value
|
||||
* of `v`. If `v` < 256, it is assumed to be 2 nibbles, otherwise 4.
|
||||
*/
|
||||
export function toHex(v: byte | word | number, n?: number) {
|
||||
if (!n) {
|
||||
n = v < 256 ? 2 : 4;
|
||||
}
|
||||
@ -66,7 +88,11 @@ export function toHex(v, n) {
|
||||
return result;
|
||||
}
|
||||
|
||||
export function toBinary(v) {
|
||||
/**
|
||||
* Returns a string of 8 binary digits.
|
||||
* @param v the value to encode
|
||||
*/
|
||||
export function toBinary(v: byte) {
|
||||
var result = '';
|
||||
for (var idx = 0; idx < 8; idx++) {
|
||||
result = bin_digits[v & 0x01] + result;
|
||||
@ -75,29 +101,36 @@ export function toBinary(v) {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of a query parameter or the empty string if it does not
|
||||
* exist.
|
||||
* @param name the parameter name. Note that `name` must not have any RegExp
|
||||
* meta-characters except '[' and ']' or it will fail.
|
||||
*/
|
||||
// From http://www.netlobo.com/url_query_string_javascript.html
|
||||
export function gup( name )
|
||||
{
|
||||
name = name.replace(/[[]/,'\\[').replace(/[\]]/,'\\]');
|
||||
var regexS = '[\\?&]'+name+'=([^&#]*)';
|
||||
var regex = new RegExp( regexS );
|
||||
var results = regex.exec( window.location.href );
|
||||
if( !results )
|
||||
export function gup(name: string) {
|
||||
name = name.replace(/[[]/, '\\[').replace(/[\]]/, '\\]');
|
||||
var regexS = '[\\?&]' + name + '=([^&#]*)';
|
||||
var regex = new RegExp(regexS);
|
||||
var results = regex.exec(window.location.href);
|
||||
if (!results)
|
||||
return '';
|
||||
else
|
||||
return results[1];
|
||||
}
|
||||
|
||||
/** Returns the URL fragment. */
|
||||
export function hup() {
|
||||
var regex = new RegExp('#(.*)');
|
||||
var results = regex.exec(window.location.hash);
|
||||
if ( !results )
|
||||
if (!results)
|
||||
return '';
|
||||
else
|
||||
return results[1];
|
||||
}
|
||||
|
||||
export function numToString(num) {
|
||||
/** Packs a 32-bit integer into a string in little-endian order. */
|
||||
export function numToString(num: number) {
|
||||
let result = '';
|
||||
for (let idx = 0; idx < 4; idx++) {
|
||||
result += String.fromCharCode(num & 0xff);
|
99
test/js/util.test.ts
Normal file
99
test/js/util.test.ts
Normal file
@ -0,0 +1,99 @@
|
||||
/** @fileoverview Test for utils.ts. */
|
||||
|
||||
import { allocMem, allocMemPages, numToString, testables, toBinary, toHex } from "../../js/util";
|
||||
|
||||
describe('garbage', () => {
|
||||
it('returns 0 <= x <= 255', () => {
|
||||
for (let i = 0; i < 1024; i++) {
|
||||
expect(testables.garbage()).toBeGreaterThanOrEqual(0);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('allocMem', () => {
|
||||
it('returns an array of the correct size', () => {
|
||||
expect(allocMem(2048).length).toBe(2048);
|
||||
|
||||
});
|
||||
it('has 0xff and 0x00 patterns', () => {
|
||||
let memory = allocMem(2048);
|
||||
expect(memory[0]).toBe(0xff);
|
||||
expect(memory[1]).toBe(0xff);
|
||||
expect(memory[2]).toBe(0x00);
|
||||
expect(memory[3]).toBe(0x00);
|
||||
expect(memory[4]).toBe(0xff);
|
||||
});
|
||||
it('has garbage in the right places', () => {
|
||||
let memory = allocMem(0x800);
|
||||
for (let i = 0; i < 0x800; i += 0x200) {
|
||||
let passed = memory[i + 0x28] != 0xff
|
||||
&& memory[i + 0x29] != 0xff
|
||||
&& memory[i + 0x68] != 0xff
|
||||
&& memory[i + 0x69] != 0xff;
|
||||
if (passed) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
fail('garbage not found');
|
||||
});
|
||||
});
|
||||
|
||||
describe('allocMemPages', () => {
|
||||
it('allocates 256 * the size', () => {
|
||||
expect(allocMemPages(5).length).toBe(5 * 256);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toHex', () => {
|
||||
it('converts an odd number of characters', () => {
|
||||
expect(toHex(0xfedcb, 5)).toEqual("FEDCB");
|
||||
});
|
||||
it('correctly guesses byte values', () => {
|
||||
expect(toHex(0xa5)).toEqual("A5");
|
||||
});
|
||||
it('correctly guesses word values', () => {
|
||||
expect(toHex(0x1abc)).toEqual("1ABC");
|
||||
});
|
||||
it('only uses the bottom work of larger values', () => {
|
||||
expect(toHex(0xabcdef)).toEqual("CDEF");
|
||||
});
|
||||
it('correctly prepends zeros', () => {
|
||||
expect(toHex(0xa5, 4)).toEqual("00A5");
|
||||
});
|
||||
});
|
||||
|
||||
describe('toBinary', () => {
|
||||
it('has 8 digits for zero', () => {
|
||||
expect(toBinary(0x00)).toEqual("00000000");
|
||||
});
|
||||
it('correctly sets bits', () => {
|
||||
expect(toBinary(0xa5)).toEqual("10100101");
|
||||
});
|
||||
});
|
||||
|
||||
describe('gup', () => {
|
||||
// untestable due to direct reference to window.location
|
||||
});
|
||||
|
||||
describe('hup', () => {
|
||||
// untestable due to direct reference to window.location
|
||||
});
|
||||
|
||||
describe('numToString', () => {
|
||||
it('packs a zero byte into a string of all zeros', () => {
|
||||
expect(numToString(0x00)).toEqual("\0\0\0\0");
|
||||
});
|
||||
it('packs a byte in the printable ASCII range into a zero-padded string',
|
||||
() => {
|
||||
expect(numToString(0x41)).toEqual("A\0\0\0");
|
||||
});
|
||||
it('packs a word into a string', () => {
|
||||
expect(numToString(0x4142)).toEqual("BA\0\0");
|
||||
});
|
||||
it('packs a 32-bit value into a string', () => {
|
||||
expect(numToString(0x41424344)).toEqual("DCBA");
|
||||
});
|
||||
it('ignores more than 32 bits', () => {
|
||||
expect(numToString(0x4142434445)).toEqual("EDCB");
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user