mirror of
https://github.com/rzzzwilson/pymlac.git
synced 2025-06-10 09:32:41 +00:00
As copied from code.google.com/rzzzwilson.
This commit is contained in:
20
vimlac/Makefile
Normal file
20
vimlac/Makefile
Normal file
@@ -0,0 +1,20 @@
|
||||
OFILES = vimlac.o cpu.o dcpu.o ptr.o ptp.o memory.o kb.o ttyin.o ttyout.o trace.o
|
||||
|
||||
CFLAGS=-shared -fPIC -O2 -Wall -ansi -pedantic -std=c99 -g
|
||||
|
||||
test: vimlac.py vimlac
|
||||
#python vimlac.py -ptr test_add.ptp -r 040 -r 0100
|
||||
vimlac
|
||||
|
||||
vimlac: ${OFILES} Makefile
|
||||
#gcc -o vimlac.so -shared -fPIC ${OFILES}
|
||||
gcc -o vimlac ${OFILES}
|
||||
|
||||
test_ptr: test_ptr.c ptr.c ptr.h Makefile
|
||||
gcc -o test_ptr ptr.c test_ptr.c
|
||||
|
||||
test_memory: test_memory.c memory.c memory.h Makefile
|
||||
gcc -o test_memory memory.c test_memory.c
|
||||
|
||||
clean:
|
||||
rm -f *.pyc *.o vimlac test_ptr test_memory *~
|
||||
1560
vimlac/cpu.c
Executable file
1560
vimlac/cpu.c
Executable file
File diff suppressed because it is too large
Load Diff
26
vimlac/cpu.h
Executable file
26
vimlac/cpu.h
Executable file
@@ -0,0 +1,26 @@
|
||||
/******************************************************************************\
|
||||
* cpu.h *
|
||||
* ------- *
|
||||
\******************************************************************************/
|
||||
|
||||
#ifndef CPU_H
|
||||
#define CPU_H
|
||||
|
||||
#include "vimlac.h"
|
||||
|
||||
/******
|
||||
* Exported functions.
|
||||
******/
|
||||
|
||||
void cpu_start(void);
|
||||
void cpu_stop(void);
|
||||
int cpu_execute_one(void);
|
||||
WORD cpu_get_AC(void);
|
||||
WORD cpu_get_L(void);
|
||||
WORD cpu_get_PC(void);
|
||||
WORD cpu_get_prev_PC(void);
|
||||
void cpu_set_PC(WORD pc);
|
||||
void cpu_set_DS(WORD ds);
|
||||
|
||||
|
||||
#endif
|
||||
145
vimlac/memory.c
Normal file
145
vimlac/memory.c
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Implementation of vimlac memory.
|
||||
*
|
||||
* Memory size is a compile-time constant.
|
||||
* The code here handles indirect memory references as will
|
||||
* as the 8 auto-index registers at 010-017 in every 2K block.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "memory.h"
|
||||
|
||||
|
||||
/*****
|
||||
* constants for the memory
|
||||
******/
|
||||
|
||||
/* size of memory is 16K words */
|
||||
#define ADDR_MASK 0x3fff
|
||||
|
||||
/* mask and limits to check if address is auto-index register */
|
||||
#define INDEX_MASK 03777
|
||||
#define LOWER_INDEX 010
|
||||
#define HIGHER_INDEX 017
|
||||
|
||||
/*****
|
||||
* State variables for the memory emulation
|
||||
******/
|
||||
|
||||
#define ROM_START 040
|
||||
#define ROM_END 077
|
||||
#define ROM_SIZE (ROM_END - ROM_START + 1)
|
||||
|
||||
static bool rom_readonly = false;
|
||||
|
||||
/* the physical memory */
|
||||
static WORD memory[MEM_SIZE];
|
||||
|
||||
/******
|
||||
* Macro to decide if we are using an auto-index register
|
||||
******/
|
||||
|
||||
#define AUTO_INDEX(a) (((a & INDEX_MASK) >= LOWER_INDEX) && ((a & INDEX_MASK) <= HIGHER_INDEX))
|
||||
|
||||
|
||||
WORD
|
||||
mem_get(WORD address, bool indirect)
|
||||
{
|
||||
WORD a = address & ADDR_MASK; /* wrap address to physical memory */
|
||||
WORD result;
|
||||
|
||||
if (indirect && AUTO_INDEX(a)) /* if auto-index and indirect, increment */
|
||||
memory[a] = (memory[a] + 1) & WORD_MASK;
|
||||
|
||||
result = memory[a & ADDR_MASK];
|
||||
if (indirect)
|
||||
result = memory[result & ADDR_MASK];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
mem_put(WORD address, bool indirect, WORD value)
|
||||
{
|
||||
WORD a = address & ADDR_MASK; /* wrap address to physical memory */
|
||||
|
||||
if (indirect && AUTO_INDEX(a)) /* if auto-index and indirect, increment */
|
||||
memory[a] = (memory[a] + 1) & WORD_MASK;
|
||||
|
||||
if (indirect)
|
||||
a = memory[a & ADDR_MASK];
|
||||
|
||||
memory[a & ADDR_MASK] = value;
|
||||
}
|
||||
|
||||
void
|
||||
mem_clear(void)
|
||||
{
|
||||
if (rom_readonly)
|
||||
{ /* save the ROM contents */
|
||||
WORD rom[ROM_SIZE];
|
||||
|
||||
memcpy(rom, &memory[ROM_START], sizeof(WORD)*ROM_SIZE);
|
||||
memset(memory, 0, sizeof(WORD)*MEM_SIZE);
|
||||
memcpy(&memory[ROM_START], rom, sizeof(WORD)*ROM_SIZE);
|
||||
}
|
||||
else
|
||||
memset(memory, 0, sizeof(WORD)*MEM_SIZE);
|
||||
}
|
||||
|
||||
void
|
||||
mem_set_rom(WORD *rom)
|
||||
{
|
||||
rom_readonly = true;
|
||||
memcpy(&memory[ROM_START], rom, sizeof(WORD)*ROM_SIZE);
|
||||
}
|
||||
|
||||
void
|
||||
mem_set_rom_readonly(bool readonly)
|
||||
{
|
||||
rom_readonly = readonly;
|
||||
}
|
||||
|
||||
void
|
||||
mem_load_core(char *filename)
|
||||
{
|
||||
FILE *fd = fopen(filename, "rb");
|
||||
WORD addr;
|
||||
|
||||
for (addr = 0; addr < MEM_SIZE; ++addr)
|
||||
{
|
||||
unsigned char byte1;
|
||||
unsigned char byte2;
|
||||
WORD value;
|
||||
|
||||
fread(&byte1, 1, 1, fd);
|
||||
fread(&byte2, 1, 1, fd);
|
||||
value = (byte1 << 8) + byte2;
|
||||
memory[addr] = value;
|
||||
}
|
||||
|
||||
fclose(fd);
|
||||
}
|
||||
|
||||
void
|
||||
mem_save_core(char *filename)
|
||||
{
|
||||
FILE *fd = fopen(filename, "wb");
|
||||
WORD addr;
|
||||
|
||||
/* write memory in bytes, get around endian problems */
|
||||
for (addr = 0; addr < MEM_SIZE; ++addr)
|
||||
{
|
||||
unsigned char byte;
|
||||
WORD value;
|
||||
|
||||
value = memory[addr];
|
||||
byte = (value >> 8) & 0xff;
|
||||
fwrite(&byte, 1, 1, fd);
|
||||
byte = value & 0xff;
|
||||
fwrite(&byte, 1, 1, fd);
|
||||
}
|
||||
|
||||
fclose(fd);
|
||||
}
|
||||
21
vimlac/memory.h
Normal file
21
vimlac/memory.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Interface for the vimlac memory.
|
||||
*/
|
||||
|
||||
#ifndef MEMORY_H
|
||||
#define MEMORY_H
|
||||
|
||||
#include "vimlac.h"
|
||||
|
||||
#define MEM_SIZE 0x4000
|
||||
|
||||
WORD mem_get(WORD address, bool indirect);
|
||||
void mem_put(WORD address, bool indirect, WORD value);
|
||||
|
||||
void mem_clear(void);
|
||||
void mem_set_rom(WORD *rom);
|
||||
void mem_set_rom_readonly(bool readonly);
|
||||
void mem_load_core(char *filename);
|
||||
void mem_save_core(char *filename);
|
||||
|
||||
#endif
|
||||
124
vimlac/ptr.c
Normal file
124
vimlac/ptr.c
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Implementation for the vimlac PTR (papertape reader).
|
||||
*/
|
||||
|
||||
#include "vimlac.h"
|
||||
#include "ptr.h"
|
||||
|
||||
|
||||
/*****
|
||||
* constants for the PTR device
|
||||
*
|
||||
* The device reads at 300 chars/second, so we work out how many
|
||||
* machine cycles data is ready/notready at a 30%/70% ready cycle.
|
||||
******/
|
||||
|
||||
#define CHARS_PER_SECOND 300
|
||||
#define CYCLES_PER_CHAR (CPU_HERZ / CHARS_PER_SECOND)
|
||||
#define READY_CYCLES (int)((3 * CYCLES_PER_CHAR) / 10)
|
||||
#define NOT_READY_CYCLES (int)((7 * CYCLES_PER_CHAR) / 10)
|
||||
|
||||
#define PTR_EOF 0377
|
||||
|
||||
/*****
|
||||
* State variables for the PTR device
|
||||
******/
|
||||
|
||||
static bool motor_on = false;
|
||||
static bool device_ready = false;
|
||||
static FILE *open_file;
|
||||
static char *filename = NULL;
|
||||
static bool at_eof = false;
|
||||
static BYTE value = PTR_EOF;
|
||||
static long cycle_count = 0;
|
||||
|
||||
|
||||
int ptr_mount(char *fname)
|
||||
{
|
||||
filename = fname;
|
||||
open_file = fopen(fname, "rb");
|
||||
if (open_file == NULL)
|
||||
{
|
||||
ptr_dismount();
|
||||
return errno;
|
||||
}
|
||||
motor_on = false;
|
||||
device_ready = false;
|
||||
at_eof = false;
|
||||
value = PTR_EOF;
|
||||
cycle_count = NOT_READY_CYCLES;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void ptr_dismount(void)
|
||||
{
|
||||
if (open_file)
|
||||
if (fclose(open_file) != 0)
|
||||
filename = NULL;
|
||||
open_file = NULL;
|
||||
motor_on = false;
|
||||
device_ready = true;
|
||||
at_eof = true;
|
||||
value = PTR_EOF;
|
||||
}
|
||||
|
||||
|
||||
void ptr_start(void)
|
||||
{
|
||||
motor_on = true;
|
||||
device_ready = false;
|
||||
cycle_count = NOT_READY_CYCLES;
|
||||
}
|
||||
|
||||
|
||||
void ptr_stop(void)
|
||||
{
|
||||
motor_on = false;
|
||||
cycle_count = NOT_READY_CYCLES;
|
||||
}
|
||||
|
||||
|
||||
int ptr_read(void)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
bool ptr_ready(void)
|
||||
{
|
||||
return device_ready;
|
||||
}
|
||||
|
||||
|
||||
void ptr_tick(long cycles)
|
||||
{
|
||||
/* if no state change */
|
||||
if (!motor_on || at_eof || open_file == NULL)
|
||||
return;
|
||||
|
||||
/* tape in, motor on */
|
||||
cycle_count -= cycles;
|
||||
if (cycle_count <= 0L)
|
||||
{
|
||||
if (device_ready)
|
||||
{
|
||||
device_ready = false;
|
||||
cycle_count += NOT_READY_CYCLES;
|
||||
value = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
device_ready = true;
|
||||
cycle_count += READY_CYCLES;
|
||||
if (fread(&value, sizeof(BYTE), 1, open_file) != 1)
|
||||
{ /* assume EOF on file, dismount tape */
|
||||
fclose(open_file);
|
||||
open_file = NULL;
|
||||
at_eof = true;
|
||||
value = PTR_EOF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
16
vimlac/ptr.h
Normal file
16
vimlac/ptr.h
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Interface for the vimlac PTR (papertape reader).
|
||||
*/
|
||||
|
||||
#ifndef PTR_H
|
||||
#define PTR_H
|
||||
|
||||
int ptr_mount(char *fname);
|
||||
void ptr_dismount(void);
|
||||
void ptr_start(void);
|
||||
void ptr_stop(void);
|
||||
int ptr_read(void);
|
||||
void ptr_tick(long cycles);
|
||||
bool ptr_ready(void);
|
||||
|
||||
#endif
|
||||
138
vimlac/test_memory.c
Normal file
138
vimlac/test_memory.c
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Test the MEMORY implementation.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "vimlac.h"
|
||||
#include "memory.h"
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
WORD addr;
|
||||
WORD result;
|
||||
FILE *fd;
|
||||
|
||||
// test the "memory clear" function
|
||||
mem_clear();
|
||||
for (addr = 0; addr < MEM_SIZE; ++addr)
|
||||
assert(mem_get(addr, false) == 0);
|
||||
|
||||
// write some data to memory, check written properly
|
||||
for (addr = 0; addr < MEM_SIZE; ++addr)
|
||||
mem_put(addr, false, addr);
|
||||
for (addr = 0; addr < MEM_SIZE; ++addr)
|
||||
{
|
||||
if (mem_get(addr, false) != addr)
|
||||
{
|
||||
printf("A: mem_get(0x%04x) was 0x%04x, should be 0x%04x\n",
|
||||
addr, mem_get(addr, false), addr);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (addr = 0; addr < MEM_SIZE; ++addr)
|
||||
{
|
||||
mem_put(addr, false, (MEM_SIZE - addr));
|
||||
}
|
||||
for (addr = 0; addr < MEM_SIZE; ++addr)
|
||||
{
|
||||
if (mem_get(addr, false) != (MEM_SIZE - addr))
|
||||
{
|
||||
printf("B: mem_get(0x%04x) was 0x%04x, should be 0x%04x\n",
|
||||
addr, mem_get(addr, false), addr);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// now test auto-index locations
|
||||
mem_put(0, false, 0);
|
||||
mem_put(1, false, 1);
|
||||
mem_put(2, false, 2);
|
||||
mem_put(010, false, 0);
|
||||
result = mem_get(010, true);
|
||||
if (result != 1)
|
||||
{
|
||||
printf("C: auto-index at address 010 not indirectly addressing\n");
|
||||
return 1;
|
||||
}
|
||||
result = mem_get(010, false);
|
||||
if (result != 1)
|
||||
{
|
||||
printf("D: auto-index at address 010 not incrementing\n");
|
||||
return 1;
|
||||
}
|
||||
mem_put(010, false, 0xffff);
|
||||
result = mem_get(010, true);
|
||||
if (result != 0)
|
||||
{
|
||||
printf("E: auto-index at address 010 not indirectly addressing\n");
|
||||
return 1;
|
||||
}
|
||||
result = mem_get(010, false);
|
||||
if (result != 0)
|
||||
{
|
||||
printf("F: auto-index at address 010 not incrementing\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// check 017, make sure address wrap-around works
|
||||
mem_put(0, false, 2);
|
||||
mem_put(1, false, 1);
|
||||
mem_put(2, false, 0);
|
||||
mem_put(017, false, 1);
|
||||
result = mem_get(017, true);
|
||||
if (result != 0)
|
||||
{
|
||||
printf("G: auto-index at address 017 not indirectly addressing\n");
|
||||
return 1;
|
||||
}
|
||||
result = mem_get(017, false);
|
||||
if (result != 2)
|
||||
{
|
||||
printf("H: auto-index at address 017 not incrementing\n");
|
||||
return 1;
|
||||
}
|
||||
mem_put(017, false, 0x3fff);
|
||||
result = mem_get(017, true);
|
||||
if (result != 2)
|
||||
{
|
||||
printf("I: auto-index at address 017 not indirectly addressing\n");
|
||||
return 1;
|
||||
}
|
||||
result = mem_get(017, false);
|
||||
if (result != 0x4000)
|
||||
{
|
||||
printf("J: auto-index at address 017 not incrementing\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// now save memory to a file
|
||||
mem_set_rom_readonly(false);
|
||||
mem_clear();
|
||||
for (addr = 0; addr < MEM_SIZE; ++addr)
|
||||
mem_put(addr, false, addr);
|
||||
fd = fopen("imlac.core", "wb");
|
||||
mem_save_core(fd);
|
||||
fclose(fd);
|
||||
|
||||
// clear memory and read core file back in
|
||||
mem_clear();
|
||||
fd = fopen("imlac.core", "rb");
|
||||
mem_load_core(fd);
|
||||
fclose(fd);
|
||||
for (addr = 0; addr < MEM_SIZE; ++addr)
|
||||
{
|
||||
if (mem_get(addr, false) != addr)
|
||||
{
|
||||
printf("K: mem_get(0x%04x) was 0x%04x, should be 0x%04x\n",
|
||||
addr, mem_get(addr, false), addr);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
46
vimlac/test_ptr.c
Normal file
46
vimlac/test_ptr.c
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Test the PTR implementation.
|
||||
*/
|
||||
|
||||
#include "vimlac.h"
|
||||
#include "ptr.h"
|
||||
|
||||
#define TIMEOUT 5000
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
ptr_mount("test1.ptp");
|
||||
ptr_start();
|
||||
while (true)
|
||||
{
|
||||
int timeout;
|
||||
unsigned char ch;
|
||||
|
||||
timeout = TIMEOUT;
|
||||
while (!ptr_ready())
|
||||
{
|
||||
ptr_tick(2);
|
||||
if (--timeout < 0)
|
||||
{
|
||||
printf("TIMEOUT\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
ch = ptr_read();
|
||||
printf("byte is %4.4o (0x%02x)\n", ch, ch);
|
||||
|
||||
timeout = TIMEOUT;
|
||||
while (ptr_ready())
|
||||
{
|
||||
ptr_tick(2);
|
||||
if (--timeout < 0)
|
||||
{
|
||||
printf("TIMEOUT\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
28
vimlac/vimlac.h
Normal file
28
vimlac/vimlac.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Interface for the vimlac emulator.
|
||||
*/
|
||||
|
||||
#ifndef VIMLAC_H
|
||||
#define VIMLAC_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
typedef unsigned int WORD;
|
||||
typedef unsigned char BYTE;
|
||||
|
||||
#define CPU_HERZ 1800000
|
||||
#define MEMMASK 0xffff
|
||||
#define HIGHBITMASK 0x8000
|
||||
#define WORD_MASK 0xffff
|
||||
#define OVERFLOWMASK 0x10000
|
||||
#define LOWBITMASK 0x1
|
||||
|
||||
|
||||
void error(char *msg, ...);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user