Feature: Keypad simulation!
Refactor: Tidy up constants use for machine configuration
This commit is contained in:
parent
b8265409cf
commit
501910b17f
|
|
@ -1,5 +1,11 @@
|
|||
const { logMemory, num2hex } = require('./logging.js');
|
||||
const { INITIAL_IP_ADDRESS, START_OF_DISPLAY_MEM, POINTER_TO_START_OF_DISPLAY_MEM } = require('./machine.config.js');
|
||||
const {
|
||||
INITIAL_IP_ADDRESS,
|
||||
DISPLAY_ADDR,
|
||||
KEYPAD_ADDR,
|
||||
POINTER_TO_DISPLAY,
|
||||
POINTER_TO_KEYPAD
|
||||
} = require('./machine.config.js');
|
||||
|
||||
// 1 = verbose
|
||||
// 2 = what i'm currently focusing on
|
||||
|
|
|
|||
45
cpu.js
45
cpu.js
|
|
@ -1,7 +1,17 @@
|
|||
const readline = require('readline');
|
||||
const readlineSync = require('readline-sync');
|
||||
|
||||
const { INITIAL_IP_ADDRESS, CYCLE_LIMIT } = require('./machine.config');
|
||||
const { num2hex, num2bin_4bit } = require('./logging.js');
|
||||
const {
|
||||
INITIAL_IP_ADDRESS,
|
||||
CYCLE_LIMIT,
|
||||
KEYPAD_ADDR,
|
||||
KEY_MAP,
|
||||
} = require('./machine.config');
|
||||
|
||||
const {
|
||||
num2hex,
|
||||
num2bin_4bit,
|
||||
} = require('./logging.js');
|
||||
const display = require('./display.js');
|
||||
|
||||
// STATE
|
||||
|
|
@ -272,6 +282,32 @@ function startCPU(code) {
|
|||
CPU.loadMemory(code);
|
||||
CPU.cycleCounter = 0;
|
||||
CPU.running = true;
|
||||
|
||||
// FIXME: This conflicts with single-stepping
|
||||
// (you can single-step, but keys aren't passed
|
||||
// through to the Cardiograph)
|
||||
//
|
||||
// -> The fix is maybe to remove readlineSync,
|
||||
// and instead stash the keypress into a buffer variable.*
|
||||
// Then have the stepping function check that buffer,
|
||||
// and then clear the buffer, each time it runs.
|
||||
//
|
||||
// * If it's in the set of keys that are relevant
|
||||
// to single-stepping.
|
||||
|
||||
// Start listening for keypresses...
|
||||
readline.emitKeypressEvents(process.stdin);
|
||||
if (process.stdin.setRawMode != null) {
|
||||
process.stdin.setRawMode(true);
|
||||
}
|
||||
process.stdin.on('keypress', (str, key) => { // TODO: is it possible to turn this off again?
|
||||
if (key.sequence === '\x03') process.exit();
|
||||
|
||||
str = str.toUpperCase();
|
||||
if (str in KEY_MAP) {
|
||||
CPU.memory[KEYPAD_ADDR] = KEY_MAP[str];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -304,7 +340,7 @@ async function stepCPU(debug = false) {
|
|||
* @param {Boolean} [debug] - Enable/disable debugging printouts
|
||||
* @param {Number} [clockSpeed] - CPU clock speed in milliseconds
|
||||
**/
|
||||
exports.runProgram = (code, debug = false, clockSpeed = 10) => {
|
||||
exports.runProgram = (code, debug = false, clockSpeed = 100) => {
|
||||
startCPU(code);
|
||||
// Animate the output by pausing between steps
|
||||
const loop = setInterval(async () => {
|
||||
|
|
@ -348,7 +384,8 @@ function logCPUState(debug = false) {
|
|||
console.log('Mnemonic:', CPU.currentInstruction.mnemonic);
|
||||
console.log(`Machine: $${num2hex(CPU.currentInstruction.opcode)} $${num2hex(CPU.currentInstruction.operand)}`);
|
||||
console.log();
|
||||
console.log(`IP: $${num2hex(CPU.IP)} Acc: $${num2hex(CPU.Acc)} NZOC: ${num2bin_4bit(CPU.FLAGS)} ${CPU.running ? "running" : "halted" }`);
|
||||
console.log(`IP: $${num2hex(CPU.IP)} Acc: $${num2hex(CPU.Acc)} NZOC: ${num2bin_4bit(CPU.FLAGS)}`);
|
||||
console.log(`KEY: $${num2hex(CPU.memory[KEYPAD_ADDR])} ${CPU.running ? "running" : "halted" }`);
|
||||
console.log();
|
||||
console.groupEnd();
|
||||
};
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
const { POINTER_TO_START_OF_DISPLAY_MEM } = require('./machine.config');
|
||||
const { POINTER_TO_DISPLAY } = require('./machine.config');
|
||||
const { num2hex } = require('./logging.js');
|
||||
|
||||
/**
|
||||
|
|
@ -6,7 +6,7 @@ const { num2hex } = require('./logging.js');
|
|||
* @param {Uint8Array} mem - CPU memory
|
||||
**/
|
||||
const printDisplay = (mem) => {
|
||||
const disp = mem[POINTER_TO_START_OF_DISPLAY_MEM];
|
||||
const disp = mem[POINTER_TO_DISPLAY];
|
||||
for (let i = disp; i < disp + 16; i += 4) {
|
||||
console.log(`${num2hex(mem[i])} ${num2hex(mem[i+1])} ${num2hex(mem[i+2])} ${num2hex(mem[i+3])}`);
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@ const printDisplay = (mem) => {
|
|||
* @param {Uint8Array} mem - CPU memory
|
||||
**/
|
||||
const prettyPrintDisplay = (mem) => {
|
||||
const disp = mem[POINTER_TO_START_OF_DISPLAY_MEM];
|
||||
const disp = mem[POINTER_TO_DISPLAY];
|
||||
const num2pic = (n) => n > 0 ? '⚫' : '⚪';
|
||||
for (let i = disp; i < disp + 16; i += 4) {
|
||||
console.log(`${num2pic(mem[i])}${num2pic(mem[i+1])}${num2pic(mem[i+2])}${num2pic(mem[i+3])}`);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,26 @@
|
|||
module.exports = {
|
||||
"START_OF_DISPLAY_MEM": 0,
|
||||
"POINTER_TO_START_OF_DISPLAY_MEM": 32,
|
||||
"POINTER_TO_START_OF_KEYPAD_MEM": 33,
|
||||
"INITIAL_IP_ADDRESS": 48,
|
||||
|
||||
// Use these in CPU:
|
||||
"DISPLAY_ADDR": 0,
|
||||
"KEYPAD_ADDR": 32,
|
||||
// Store the `X_ADDR`s at these addresses when assembling:
|
||||
"POINTER_TO_DISPLAY": 33,
|
||||
"POINTER_TO_KEYPAD": 34,
|
||||
|
||||
"KEY_MAP": {
|
||||
// Same layout as COSMAC VIP / CHIP-8
|
||||
// (This object maps qwerty keys to hex keys
|
||||
// so that they are arranged in the same layout
|
||||
// as the real keypad)
|
||||
'1':'1', '2':'2', '3':'3', '4':'C',
|
||||
'Q':'4', 'W':'5', 'E':'6', 'R':'D',
|
||||
'A':'7', 'S':'8', 'D':'9', 'F':'E',
|
||||
'Z':'A', 'X':'0', 'C':'B', 'V':'F',
|
||||
},
|
||||
|
||||
// max number of times to step the CPU,
|
||||
// to stop endless loops
|
||||
// 0 = infinite
|
||||
"CYCLE_LIMIT": 128,
|
||||
"CYCLE_LIMIT": 256,
|
||||
}
|
||||
31
readme.md
31
readme.md
|
|
@ -69,13 +69,36 @@ With single stepping + verbose debugging output:
|
|||
## Memory map / Peripherals
|
||||
|
||||
- `00-0F` - display (4x4)
|
||||
- `10-1F` - keypad? (details TBD)
|
||||
- `20 ` - pointer to display memory
|
||||
- `21 ` - pointer to keypad memory
|
||||
- `22-2F` - reserved for future use / variable storage
|
||||
- `10-19` - reserved for future use
|
||||
- `20 ` - keypad - value of the most recent keypress
|
||||
- `21 ` - pointer to display memory
|
||||
- `22 ` - pointer to keypad memory
|
||||
- `23-2F` - reserved for future use / variable storage
|
||||
- `30 ` - initial value for IP
|
||||
- `30-FF` - free
|
||||
|
||||
### Keypad
|
||||
|
||||
The value of the latest keypress on a hex keypad is stored at `$20`.
|
||||
(The keypad can also be relocated by changing the value of the pointer-to-keypad at `$22`.)
|
||||
|
||||
The keypad uses the same layout as the COSMAC VIP (and CHIP-8):
|
||||
|
||||
```
|
||||
1 2 3 C
|
||||
4 5 6 D
|
||||
7 8 9 E
|
||||
A 0 B F
|
||||
```
|
||||
The CPU simulator maps the following Qwerty keys onto those values:
|
||||
|
||||
```
|
||||
1 2 3 4
|
||||
Q W E R
|
||||
A S D F
|
||||
Z X C V
|
||||
```
|
||||
|
||||
## Assembly language
|
||||
|
||||
ADD $01 ; comments follow a `;`
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
const readline = require('readline');
|
||||
|
||||
readline.emitKeypressEvents(process.stdin);
|
||||
|
||||
if (process.stdin.setRawMode != null) {
|
||||
process.stdin.setRawMode(true);
|
||||
}
|
||||
|
||||
process.stdin.on('keypress', (str, key) => {
|
||||
console.log(str)
|
||||
console.log(key)
|
||||
if (key.sequence === '\x03') process.exit();
|
||||
})
|
||||
|
||||
let i = 0;
|
||||
const loop = setInterval(async () => {
|
||||
console.log('loop #', i);
|
||||
if (i > 10) clearInterval(loop);
|
||||
i += 1;
|
||||
}, 250);
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
;; Test keypad input
|
||||
;; 2023-08-16
|
||||
|
||||
; The latest keypress is shown in the top left corner of the display.
|
||||
; A loop counter is shown in the top right corner; the program ends when it reaches zero.
|
||||
; (For a 4x4 display.)
|
||||
|
||||
#LOOPCOUNT $80
|
||||
|
||||
#Z 2 ; the zero flag is #2
|
||||
#keypad $20 ; magic memory location containing latest key pressed
|
||||
#loopIter $FF ; address of loop iterator
|
||||
#iterPx $03 ; where to display iterator
|
||||
#keyPx $00 ; where to display key
|
||||
|
||||
LDA #LOOPCOUNT
|
||||
STO #loopIter
|
||||
|
||||
@loop
|
||||
LDA (#loopIter)
|
||||
STO #iterPx ; display loop iterator
|
||||
LDA (#keypad)
|
||||
STO #keyPx ; display latest keypress
|
||||
LDA (#loopIter)
|
||||
SUB 1
|
||||
STO #loopIter
|
||||
FTG #Z
|
||||
FHP #Z
|
||||
END
|
||||
JMP @loop
|
||||
Loading…
Reference in New Issue