mirror of
https://github.com/livingcomputermuseum/UniBone.git
synced 2026-01-29 13:11:11 +00:00
PRU1 code split into multiple images 1. test functions 2. UNIBUS operation PRU1 bus latch interface Write byte/bits access not with MACROS (random optimizer influence), now with *_helper() procedures. Same timing, more determinism, much code saving. Nono more ASM code to write PRU0 XFER area. demo: menu to test UNIBUS signals directly rework "Arbitration" logic: now 3-fold Rework of UNIBUs arbtiration: NONE/CLIENT/MASTER - no Arbitrator (SACK penidng for 11/34 Konsole) (NONE) - phyiscal PDP_11 CPU is Arbitrator (CLIENT) - UniBone implements Arbitrator (MASTER) - Same PRU code loop handles all arbitration types PRU buslatch timing slower, for some problematic PCBs More aggressive bus latch selftest (mixed patterns, running on PRU now) Refinement of ready-to-run scripts - Adapted to changed "demo" menu - new name scheme <OS>_<boot- drive>_<PDP-11CPU> indicates - which OS is run - which disk emulation is used and what is the boot device - what is the (minimum) PDP-11 to run that Merged in Joshs DMA timing for 11/84 UNIBUS master cycles waits 350 us before MSYN, instead 150. Merged in Joshs DMA request queue multiple devices canrequest INTR and DMAs concurrently, will be put on the bus sequentially Merged in Joshs MSCP driver - Build RT-11v5.5 for MSCP - added boot loader "du.lst" MSCP run scrips 2.11BSD on MSCP on PDP-11/44 RT11 on MSCP Fix: image file sizing Disk image file exptend automatically if block beyond current file end is written
680 lines
20 KiB
C++
680 lines
20 KiB
C++
/* memoryimage.cpp - reading and saving several memory file formats
|
|
|
|
Copyright (c) 2017, Joerg Hoppe, j_hoppe@t-online.de, www.retrocmp.com
|
|
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions
|
|
are met:
|
|
|
|
- Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
- Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
|
|
- Neither the name of the copyright holder nor the names of its
|
|
contributors may be used to endorse or promote products derived from
|
|
this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
|
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
12-nov-2018 JH entered beta phase
|
|
18-Jun-2017 JH created
|
|
|
|
*/
|
|
#define _MEMORYIMAGE_CPP_
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <assert.h>
|
|
|
|
#include "logger.hpp"
|
|
#include "memoryimage.hpp" // own
|
|
|
|
// single multi purpose memory image buffer
|
|
memoryimage_c *membuffer;
|
|
|
|
void memoryimage_c::init() {
|
|
unsigned wordidx;
|
|
for (wordidx = 0; wordidx < MEMORY_WORD_COUNT; wordidx++) {
|
|
data.words[wordidx] = 0;
|
|
valid[wordidx] = false;
|
|
}
|
|
}
|
|
// fill valid address range with constant word
|
|
void memoryimage_c::fill(uint16_t fillword) {
|
|
unsigned wordidx;
|
|
for (wordidx = 0; wordidx < MEMORY_WORD_COUNT; wordidx++)
|
|
if (valid[wordidx])
|
|
data.words[wordidx] = fillword;
|
|
}
|
|
|
|
// return # of valid words
|
|
unsigned memoryimage_c::get_word_count(void) {
|
|
unsigned result = 0;
|
|
unsigned wordidx;
|
|
for (wordidx = 0; wordidx < MEMORY_WORD_COUNT; wordidx++)
|
|
if (valid[wordidx])
|
|
result++;
|
|
return result;
|
|
}
|
|
|
|
// return first and last valid adddres
|
|
// first > last: no data set
|
|
void memoryimage_c::get_addr_range(unsigned *first, unsigned* last) {
|
|
*first = 0x3FFFF;
|
|
*last = 0;
|
|
unsigned wordidx;
|
|
for (wordidx = 0; wordidx < MEMORY_WORD_COUNT; wordidx++)
|
|
if (valid[wordidx]) {
|
|
unsigned addr = 2 * wordidx;
|
|
if (addr < *first)
|
|
*first = addr;
|
|
if (addr > *last)
|
|
*last = addr;
|
|
}
|
|
}
|
|
|
|
// set valid address range to [first..last]
|
|
void memoryimage_c::set_addr_range(unsigned first, unsigned last) {
|
|
unsigned wordidx;
|
|
assert(first <= last);
|
|
assert(last < 2*MEMORY_WORD_COUNT);
|
|
for (wordidx = 0; wordidx < MEMORY_WORD_COUNT; wordidx++) {
|
|
unsigned addr = 2 * wordidx;
|
|
if (addr < first || last < addr)
|
|
valid[wordidx] = false;
|
|
else
|
|
valid[wordidx] = true;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* can set bytes at odd addresses
|
|
*/
|
|
void memoryimage_c::put_byte(unsigned addr, unsigned b) {
|
|
unsigned w;
|
|
unsigned baseaddr = addr & ~1; // clear bit 0
|
|
assert_address(addr);
|
|
w = get_word(baseaddr);
|
|
if (baseaddr == addr) // set lsb
|
|
w = (w & 0xff00) | b;
|
|
else
|
|
w = (w & 0xff) | (b << 8);
|
|
put_word(addr, w);
|
|
}
|
|
|
|
/* load a binary memory image into mem[0] upwards
|
|
* file format: LSB of a word first.
|
|
* all relevant platforms (x64, ARM) are little endian, so
|
|
* the file can just be read into words[]
|
|
* result: false = error, true = OK
|
|
* caller must call init() first !
|
|
*/
|
|
bool memoryimage_c::load_binary(const char *fname) {
|
|
FILE *fin;
|
|
unsigned wordidx, n;
|
|
fin = fopen(fname, "rb");
|
|
if (!fin) {
|
|
printf("%s\n",fileErrorText("Error opening file %s for read", fname));
|
|
return false;
|
|
}
|
|
// try to read max address range, shorter files are OK
|
|
n = fread((void *) data.words, 2, MEMORY_WORD_COUNT, fin);
|
|
for (wordidx = 0; wordidx < n; wordidx++)
|
|
valid[wordidx] = true;
|
|
fclose(fin);
|
|
return true;
|
|
}
|
|
|
|
void memoryimage_c::save_binary(const char *fname, unsigned bytecount) {
|
|
FILE *fout;
|
|
unsigned wordcount = (bytecount + 1) / 2;
|
|
unsigned n;
|
|
fout = fopen(fname, "wb");
|
|
if (!fout) {
|
|
printf("%s\n",fileErrorText("Error opening file %s for write", fname));
|
|
return;
|
|
}
|
|
// try to read max address range, shorter files are OK
|
|
n = fwrite((void *) data.words, 2, wordcount, fout);
|
|
fclose(fout);
|
|
|
|
if (n != wordcount) {
|
|
WARNING("mem_save_binary(): tried to write %u words, only %u successful.", wordcount,
|
|
n);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* textfile with words separated by white string.
|
|
* 1st word = address, following words data.
|
|
* every char other than [0-7] interpreted as white space
|
|
* Prints "hello world":
|
|
| 002000: 012702 177564 012701 002032 112100 001405 110062 000002
|
|
| 002020: 105712 100376 000771 000000 000763 062510 066154 026157
|
|
| 002040: 073440 071157 062154 006441
|
|
* Or:
|
|
| deposit 002000 012702
|
|
| deposit 002002 177564
|
|
| deposit 002004 012701
|
|
| deposit 002006 002032
|
|
| deposit 002010 112100
|
|
| deposit 002012 001405
|
|
| deposit 002014 110062
|
|
| deposit 002016 000002
|
|
| deposit 002020 105712
|
|
| deposit 002022 100376
|
|
| deposit 002024 000771
|
|
| deposit 002026 000000
|
|
| deposit 002030 000763
|
|
| deposit 002032 062510
|
|
| deposit 002034 066154
|
|
| deposit 002036 026157
|
|
| deposit 002040 073440
|
|
| deposit 002042 071157
|
|
| deposit 002044 062154
|
|
| deposit 002046 006441
|
|
*
|
|
* result: false = error, true = OK
|
|
*/
|
|
bool memoryimage_c::load_addr_value_text(const char *fname) {
|
|
FILE *fin;
|
|
char linebuff[1024];
|
|
char *s, *token, *endptr;
|
|
int n;
|
|
unsigned val;
|
|
unsigned addr;
|
|
|
|
fin = fopen(fname, "r");
|
|
if (!fin) {
|
|
printf("%s\n", fileErrorText("Error opening file %s for write", fname)) ;
|
|
return false;
|
|
}
|
|
entry_address = MEMORY_ADDRESS_INVALID; // not known
|
|
|
|
while (fgets(linebuff, sizeof(linebuff), fin)) {
|
|
// make everything ( including \t, \n ,..) but octal digits a whitespace
|
|
for (s = linebuff; *s; s++)
|
|
if (*s < '0' || *s > '7')
|
|
*s = ' ';
|
|
// parse all worlds, n = word index
|
|
n = 0;
|
|
// parse first token = addr
|
|
token = strtok(linebuff, " ");
|
|
/* walk through data tokens */
|
|
while (token != NULL) {
|
|
val = strtol(token, &endptr, 8);
|
|
if (n == 0) // first token = address
|
|
addr = val;
|
|
else { // following tokens are data
|
|
assert_address(addr);
|
|
put_word(addr, val);
|
|
addr += 2; // inc by word
|
|
}
|
|
n++;
|
|
token = strtok(NULL, " "); // get/test next
|
|
}
|
|
}
|
|
fclose(fin);
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Load from a macro 11 listing
|
|
|
|
|
|M9312 'DD' BOOT prom for TU58 D MACRO V05.05 Tuesday 23-Mar-99 00:02 Page 1
|
|
|
|
|
|
|
|
| 1
|
|
| 2 .title M9312 'DD' BOOT prom for TU58 DECtapeII serial tape controller (REVISED)
|
|
| 3
|
|
| 4 ; Original edition AK6DN Don North,
|
|
| 5 ; Further processed JH
|
|
| 6
|
|
| 7 ; This source code is an M9312 boot PROM for the TU58 version 23-765B9.
|
|
| 8 ;
|
|
| 9 ; This boot PROM is for the TU58 DECtapeII serial tape controller.
|
|
| 10 ;
|
|
| 11 ; Multiple units and/or CSR addresses are supported via different entry points.
|
|
| 12 ;
|
|
| 13 ; Standard devices are 82S131, Am27S13, 74S571 or other compatible bipolar
|
|
| 14 ; PROMs with a 512x4 TriState 16pin DIP architecture. This code resides in
|
|
| 15 ; the low half of the device; the top half is blank and unused.
|
|
| 16 ;
|
|
| 17 ; Alternatively, 82S129 compatible 256x4 TriState 16pin DIP devices can be
|
|
| 18 ; used, as the uppermost address line (hardwired low) is an active low chip
|
|
| 19 ; select (and will be correctly asserted low).
|
|
| 20
|
|
| 21
|
|
| 22 ;VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
|
|
| 23 ;
|
|
| 24 ; The original code in 23-765A9 is REALLY BROKEN when it comes to
|
|
| 25 ; supporting a non-std CSR other than 776500 in R1 on entry
|
|
| 26 ;
|
|
| 27 ; All the hard references to: ddrbuf, ddxcsr, ddxbuf
|
|
| 28 ; have been changed to: 2(R1), 4(R1), 6(R1)
|
|
| 29 ;
|
|
| 30 ; The one reference where 'ddrcsr' might have been used is '(R1)' instead
|
|
| 31 ; which is actually correct (but totally inconsistent with other usage).
|
|
| 32 ;
|
|
| 33 ;AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
| 34
|
|
| 35
|
|
| 36 176500 ddcsr =176500 ; std TU58 csrbase
|
|
| 37
|
|
| 38 000000 ddrcsr =+0 ; receive control
|
|
| 39 000002 ddrbuf =+2 ; receive data
|
|
| 40 000004 ddxcsr =+4 ; transmit control
|
|
| 41 000006 ddxbuf =+6 ; transmit data
|
|
| 42
|
|
| 43 000000 .asect
|
|
| 44 010000 .=10000
|
|
| 45
|
|
| 46 ; --------------------------------------------------
|
|
| 47
|
|
| 48 010000 start:
|
|
| 49
|
|
| 50 010000 000261 dd0n: sec ; boot std csr, unit zero, no diags
|
|
| 51 010002 012700 000000 dd0d: mov #0,r0 ; boot std csr, unit zero, with diags
|
|
| 52 010006 012701 176500 ddNr: mov #ddcsr,r1 ; boot std csr, unit <R0>
|
|
| 53
|
|
| 54
|
|
| 55 ; mov #101,r3 ; transmit A
|
|
| 56 ; mov r3,@#176506
|
|
| 57 ; halt
|
|
|^L M9312 'DD' BOOT prom for TU58 D MACRO V05.05 Tuesday 23-Mar-99 00:02 Page 1-1
|
|
|
|
|
|
|
|
| 58 ; call xmtch ; transmit unit number
|
|
| 59 ; halt
|
|
| 60 ; mov #132,r3 ; transmit A
|
|
| 61 ; call xmtch ; transmit unit number
|
|
| 62 ; halt
|
|
| 63 010012 012706 002000 go: mov #2000,sp ; setup a stack
|
|
| 64 010016 005004 clr r4 ; zap old return address
|
|
| 65 010020 005261 000004 inc ddxcsr(r1) ; set break bit
|
|
| 66 010024 005003 clr r3 ; data 000,000
|
|
| 67 010026 004767 000050 call xmtch8 ; transmit 8 zero chars
|
|
| 68 010032 005061 000004 clr ddxcsr(r1) ; clear break bit
|
|
| 69 010036 005761 000002 tst ddrbuf(r1) ; read/flush any stale rx char
|
|
| 70 010042 012703 004004 mov #<010*400>+004,r3 ; data 010,004
|
|
| 71 010046 004767 000034 call xmtch2 ; transmit 004 (init) and 010 (boot)
|
|
| 72 010052 010003 mov r0,r3 ; get unit number
|
|
| 73 010054 004767 000030 call xmtch ; transmit unit number
|
|
| 74
|
|
| 75 010060 005003 clr r3 ; clear rx buffer ptr
|
|
| 76 010062 105711 2$: tstb (r1) ; wait for rcv'd char available
|
|
| 77 010064 100376 bpl 2$ ; br if not yet
|
|
| 78 010066 116123 000002 movb ddrbuf(r1),(r3)+ ; store the char in buffer, bump ptr
|
|
| 79 010072 022703 001000 cmp #1000,r3 ; hit end of buffer (512. bytes)?
|
|
| 80 010076 101371 bhi 2$ ; br if not yet
|
|
| 81
|
|
| 82 ; halt
|
|
| 83 010100 005007 clr pc ; jump to bootstrap at zero
|
|
| 84
|
|
| 85 010102 xmtch8: ; transmit 4x the two chars in r3
|
|
| 86 010102 004717 jsr pc,(pc) ; recursive call for char replication
|
|
| 87 010104 xmtch4:
|
|
| 88 010104 004717 jsr pc,(pc) ; recursive call for char replication
|
|
| 89 010106 xmtch2: ; transmit 2 chars in lower r3, upper r3
|
|
| 90 010106 004717 jsr pc,(pc) ; recursive call for char replication
|
|
| 91 010110 xmtch: ; xmt char in lower r3, then swap r3
|
|
| 92 010110 105761 000004 tstb ddxcsr(r1) ; wait for xmit buffer available
|
|
| 93 010114 100375 bpl xmtch ; br if not yet
|
|
| 94 010116 110361 000006 movb r3,ddxbuf(r1) ; send the char
|
|
| 95 010122 000303 swab r3 ; swap to other char
|
|
| 96 010124 000207 return ; rts pc ; now recurse or return
|
|
| 97
|
|
| 98 000001 .end
|
|
|^L M9312 'DD' BOOT prom for TU58 D MACRO V05.05 Tuesday 23-Mar-99 00:02 Page 1-2
|
|
|Symbol table
|
|
|
|
|
|DDCSR = 176500 DDRCSR= 000000 DD0D 010002 START 010000 XMTCH4 010104
|
|
|DDNR 010006 DDXBUF= 000006 DD0N 010000 XMTCH 010110 XMTCH8 010102
|
|
|DDRBUF= 000002 DDXCSR= 000004 GO 010012 XMTCH2 010106
|
|
|
|
|
|. ABS. 010126 000 (RW,I,GBL,ABS,OVR)
|
|
| 000000 001 (RW,I,LCL,REL,CON)
|
|
|Errors detected: 0
|
|
|
|
|
|*** Assembler statistics
|
|
|
|
|
|
|
|
|Work file reads: 0
|
|
|Work file writes: 0
|
|
|Size of work file: 48 Words ( 1 Pages)
|
|
|Size of core pool: 15104 Words ( 59 Pages)
|
|
|Operating system: RT-11
|
|
|
|
|
|Elapsed time: 00:00:09.30
|
|
|DK:DDBOOT,DK:DDBOOT.LST=DK:DDBOOT
|
|
|
|
|
|
So:
|
|
- in first line, determine width of line numbers
|
|
- strip off line number in every line
|
|
- 1st octal is address, must be 16 bit
|
|
- following octals are data
|
|
- data may be 3 digits, then 8bit, or 6 digits, then 16 bit.
|
|
- multiple data per line allowed
|
|
|
|
Entry label: labels can appear with or without octal data
|
|
case 1: standalone
|
|
| 68 007776 stack = . - 2 ; stack growns down from start
|
|
| 69
|
|
| 70 start:
|
|
| 71 010000 012706 007776 mov #stack,sp ; init stack
|
|
| 72 010004 005037 177776 clr @#psw ; clear priority, allow all interupts
|
|
case 2: after octal data
|
|
| 62 ; xxx
|
|
| 63 010012 012706 002000 go: mov #2000,sp ; setup a stack
|
|
| 64 010016 005004 clr r4 ; zap old return address
|
|
| 65 010020 005261 000004 inc ddxcsr(r1) ; set break bit
|
|
*
|
|
*/
|
|
|
|
// remove trailing white space by setting a new \0
|
|
static void trim_trail(char *line) {
|
|
char *s = line + strlen(line) - 1;
|
|
|
|
while (s >= line && isspace(*s)) {
|
|
*s = 0;
|
|
s--;
|
|
}
|
|
}
|
|
|
|
static int calc_lineno_width(char *line) {
|
|
// determine end of
|
|
char *s = line;
|
|
while (*s && isspace(*s))
|
|
s++;
|
|
while (*s && isdigit(*s))
|
|
s++;
|
|
// s now on first char after first number
|
|
return s - line;
|
|
}
|
|
|
|
// return the next literal, opcode or label: alphanum, ".", "$"
|
|
static char *nxt_token(char *tp) {
|
|
static char buff[256];
|
|
char *s = buff;
|
|
while (*tp && strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890$.", toupper(*tp)))
|
|
*s++ = *tp++;
|
|
*s = 0;
|
|
return buff; // terminate
|
|
}
|
|
|
|
// entrylabel: name of label to be used as entry address
|
|
// result: false = error, true = OK
|
|
// caller must call init() first !
|
|
|
|
bool memoryimage_c::load_macro11_listing(const char *fname, const char *entrylabel) {
|
|
int lineno_fieldwidth = 0;
|
|
bool next_addr_is_entry = false;
|
|
FILE *fin;
|
|
char linebuff[1024];
|
|
char *tp; // token pointer, start of cur number
|
|
int tokenidx;
|
|
bool ready;
|
|
unsigned val;
|
|
unsigned addr, line_addr;
|
|
char *endptr;
|
|
int wlen;
|
|
|
|
fin = fopen(fname, "r");
|
|
if (!fin) {
|
|
printf("%s\n", fileErrorText("Error opening file %s", fname));
|
|
return false;
|
|
}
|
|
entry_address = MEMORY_ADDRESS_INVALID; // not (yet) known
|
|
|
|
while (fgets(linebuff, sizeof(linebuff), fin)) {
|
|
// remove trailing spaces
|
|
trim_trail(linebuff);
|
|
|
|
if (strlen(linebuff) == 0)
|
|
continue; // empty line: next line
|
|
|
|
// parse only until "Symbol table" in column 1"
|
|
if (!strncasecmp(linebuff, "Symbol table", 12))
|
|
break; // stop parsing
|
|
|
|
if (linebuff[0] != ' ' && linebuff[0] != 9)
|
|
continue; // line starts not with white space: header, no numbered line
|
|
|
|
if (lineno_fieldwidth == 0)
|
|
lineno_fieldwidth = calc_lineno_width(linebuff); // only once
|
|
tp = linebuff + lineno_fieldwidth; // skip line number, ptr to cur pos
|
|
|
|
// process all octal digit strings
|
|
tokenidx = 0; // # of number processed
|
|
ready = false;
|
|
addr = 0;
|
|
line_addr = 0 ;
|
|
while (!ready) {
|
|
while (*tp && isspace(*tp))
|
|
tp++; // skip white space
|
|
// entry label case 1: label without octal addr/data?
|
|
if (entrylabel && !strcasecmp(nxt_token(tp), entrylabel))
|
|
next_addr_is_entry = true; // fetch 1st following address
|
|
|
|
val = strtol(tp, &endptr, 8);
|
|
wlen = endptr - tp; // len if the numeric word
|
|
if (wlen == 0) {
|
|
// entrylabel case 2: label in row with addr/data?
|
|
// is next word after the entrylabel?
|
|
if (entrylabel && !strcasecmp(nxt_token(tp), entrylabel))
|
|
entry_address = line_addr;
|
|
|
|
ready = true;
|
|
} else if (wlen == 6) { // 16 bit value
|
|
if (tokenidx == 0) {
|
|
line_addr = addr = val; // 1st number: address
|
|
if (next_addr_is_entry) {
|
|
// entry label case 1: we hit the entry label before
|
|
entry_address = line_addr; //
|
|
next_addr_is_entry = false; // fulfilled
|
|
}
|
|
} else {
|
|
assert_address(addr);
|
|
put_word(addr, val);
|
|
addr += 2; // inc by word
|
|
}
|
|
tokenidx++;
|
|
tp = endptr; // skip this number
|
|
|
|
} else if (wlen == 3) { // 8 bnt value
|
|
assert_address(addr);
|
|
put_byte(addr, val);
|
|
addr++; // inc by byte
|
|
tokenidx++;
|
|
tp = endptr; // skip this number
|
|
} else
|
|
ready = true; // terminate on any unexpected
|
|
}
|
|
}
|
|
|
|
fclose(fin);
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Load a DEC Standard Absolute Papertape Image file
|
|
* result: false = error, true = OK
|
|
* caller must call init() first !
|
|
*/
|
|
bool memoryimage_c::load_papertape(const char *fname) {
|
|
FILE *fin;
|
|
int b;
|
|
int state = 0;
|
|
int block_byte_idx = 0;
|
|
int block_databyte_count = 0;
|
|
int block_byte_size, sum = 0, addr;
|
|
int stream_byte_index; // absolute index of byte in file
|
|
|
|
fin = fopen(fname, "r");
|
|
if (!fin) {
|
|
printf("%s\n", fileErrorText("Error opening file %s for read", fname));
|
|
return false;
|
|
}
|
|
|
|
entry_address = MEMORY_ADDRESS_INVALID; // not yet known
|
|
|
|
stream_byte_index = 0;
|
|
while (!feof(fin)) {
|
|
b = fgetc(fin);
|
|
// ERROR("[0x%04x] state=%d b=0x%02x sum=0x%02x block_byte_idx=%d",
|
|
// stream_byte_index, state, b, sum, block_byte_idx);
|
|
stream_byte_index++;
|
|
switch (state) {
|
|
case 0: // skip bytes until 0x01 is found
|
|
sum = 0;
|
|
if (b == 1) {
|
|
state = 1; // valid header: initialize counter and checksum
|
|
block_byte_idx = 1;
|
|
sum += b;
|
|
sum &= 0xff;
|
|
}
|
|
break;
|
|
case 1: // 0x01 is found, check for following 0x00
|
|
if (b != 0)
|
|
state = 0; // invalid start, skip bytes
|
|
else {
|
|
state = 2;
|
|
block_byte_idx++;
|
|
sum += b;
|
|
sum &= 0xff;
|
|
}
|
|
break;
|
|
case 2:
|
|
// read low count byte
|
|
block_byte_size = b;
|
|
state = 3;
|
|
sum += b;
|
|
sum &= 0xff;
|
|
block_byte_idx++;
|
|
break;
|
|
case 3:
|
|
// read high count byte
|
|
block_byte_size = block_byte_size | (b << 8);
|
|
// calculate data word count
|
|
block_databyte_count = (block_byte_size - 6);
|
|
state = 4;
|
|
sum += b;
|
|
sum &= 0xff;
|
|
block_byte_idx++;
|
|
break;
|
|
case 4:
|
|
// read addr low
|
|
addr = b;
|
|
sum += b;
|
|
sum &= 0xff;
|
|
state = 5;
|
|
block_byte_idx++;
|
|
break;
|
|
case 5:
|
|
// read addr high
|
|
addr = addr | (b << 8);
|
|
state = 6;
|
|
sum += b;
|
|
sum &= 0xff;
|
|
block_byte_idx++;
|
|
if (block_byte_idx > block_byte_size) {
|
|
fprintf(stderr, "Skipping mis-sized block with addr = %06o, size=%d", addr,
|
|
block_byte_size);
|
|
state = 0;
|
|
} else {
|
|
// block range now known
|
|
// if block size = 0: entry addr
|
|
if (block_databyte_count == 0) {
|
|
entry_address = addr;
|
|
// DEBUG(xxx, "Empty block with addr = %06o, block_byte_size=%d", addr, block_byte_size);
|
|
state = 0; // empty block. no chksum ?
|
|
}
|
|
// else INFO("Papertape: Starting data block with addr = %06o, block_byte_size=%d, databyte_count=%d",
|
|
// addr, block_byte_size, block_databyte_count);
|
|
}
|
|
break;
|
|
case 6: // read data byte
|
|
assert_address(addr);
|
|
put_byte(addr, b);
|
|
sum += b;
|
|
sum &= 0xff;
|
|
addr += 1;
|
|
block_byte_idx++;
|
|
if (block_byte_idx >= block_byte_size)
|
|
state = 7; // block end
|
|
break;
|
|
case 7:
|
|
// all words of block read, eval checksum
|
|
sum += b;
|
|
sum &= 0xff;
|
|
if (sum != 0) {
|
|
ERROR("Papertape Checksum error chksum = %02X", sum);
|
|
return false;
|
|
}
|
|
// else
|
|
// INFO("Papertape block end with correct checksum");
|
|
sum = 0;
|
|
state = 0;
|
|
}
|
|
}
|
|
// if (_this->entry_address != -1)
|
|
// INFO("Papertape entry address = %06o", _this->entry_address);
|
|
|
|
fclose(fin);
|
|
return true;
|
|
}
|
|
|
|
// show range, count ,start
|
|
void memoryimage_c::info(FILE *f) {
|
|
if (!f)
|
|
return;
|
|
unsigned first_addr, last_addr, wordcount;
|
|
unsigned addr;
|
|
|
|
first_addr = MEMORY_WORD_COUNT + 1; // invalid
|
|
last_addr = 0;
|
|
wordcount = 0;
|
|
for (addr = 0; addr < 2 * MEMORY_WORD_COUNT; addr += 2)
|
|
if (is_valid(addr)) {
|
|
if (first_addr > MEMORY_WORD_COUNT)
|
|
first_addr = addr;
|
|
last_addr = addr;
|
|
wordcount++;
|
|
}
|
|
if (wordcount == 0)
|
|
fprintf(f, "memory empty\n");
|
|
else {
|
|
fprintf(f, "memory filled from %06o to %06o with %u words", first_addr, last_addr,
|
|
wordcount);
|
|
if (entry_address >= 0)
|
|
fprintf(f, ", entry addr = %06o", entry_address);
|
|
fprintf(f, "\n");
|
|
}
|
|
}
|
|
|
|
void memoryimage_c::dump(FILE *f) {
|
|
unsigned addr;
|
|
for (addr = 0; addr < 2 * MEMORY_WORD_COUNT; addr += 2)
|
|
if (is_valid(addr))
|
|
fprintf(f, "%06o %06o\n", addr, get_word(addr));
|
|
}
|