Skip to content

Commit

Permalink
Ohio Scientific 65A Support (#57)
Browse files Browse the repository at this point in the history
Added read and write support for a format compatible with the Ohio Scientific 65A ROM monitor, which is found on Ohio Scientific 6502 systems with serial console. 

Tests are included. The formatter supports optional execution address.
  • Loading branch information
chapmajs authored Apr 5, 2023
1 parent 6bca708 commit 401ad21
Show file tree
Hide file tree
Showing 15 changed files with 878 additions and 5 deletions.
5 changes: 4 additions & 1 deletion doc/etc/README.man
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,11 @@ The Needham Electronics ASCII file format is understood for both reading
and writing.
.\" ---------- O ---------------------------------------------------------
.TP 8n
OS65A
The Ohio Scientific 65A format is understood for both reading and writing.
.TP 8n
OS65V
The Ohio Scientific hexadecimal format is understood for both reading
The Ohio Scientific 65V hexadecimal format is understood for both reading
and writing.
.\" ---------- P ---------------------------------------------------------
.TP 8n
Expand Down
9 changes: 7 additions & 2 deletions doc/html/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -265,9 +265,14 @@
<p>
.\" ---------- O ---------------------------------------------------------
<dt>
<a href="man/man5/srec_os65v.5.html" >Ohio Scientific</a> (OS65V)
<a href="man/man5/srec_os65a.5.html" >Ohio Scientific 65A</a> (OS65A)
<dd>
The Ohio Scientific hexadecimal format is understood for both reading
The Ohio Scientific 65A format is understood for both reading and writing.
<p>
<dt>
<a href="man/man5/srec_os65v.5.html" >Ohio Scientific 65V</a> (OS65V)
<dd>
The Ohio Scientific 65V hexadecimal format is understood for both reading
and writing.
<p>
.\" ---------- P ---------------------------------------------------------
Expand Down
7 changes: 6 additions & 1 deletion doc/man1/srec_cat.1
Original file line number Diff line number Diff line change
Expand Up @@ -356,8 +356,13 @@ write the file. See \f[I]srec_needham\fP(5) for a description of this
file format.
.\" ----- --output -O* -------------------------------------------------
.TP 8n
\fB\-Ohio_Scientific65A\fP
This option says to use the Ohio Scientific 65A hexadecimal format.
See \f[I]srec_os65a\fP(5) for a description of this format.
.TP 8n
\fB\-Ohio_Scientific\fP
This option says to use the Ohio Scientific hexadecimal format.
\fB\-Ohio_Scientific65V\fP
This option says to use the Ohio Scientific 65V hexadecimal format.
See \f[I]srec_os65v\fP(5) for a description of this format.
.\" ----- --output -P* -------------------------------------------------
.TP 8n
Expand Down
78 changes: 78 additions & 0 deletions doc/man5/srec_os65a.5
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
'\" t
.\" srecord - manipulate eprom load files
.\" Copyright (C) 2002, 2004, 2006-2009 Peter Miller
.\"
.\" OS65A module Copyright (c) 2022, 2023 Glitch Works, LLC
.\" http://www.glitchwrks.com/
.\"
.\" This program is free software; you can redistribute it and/or modify
.\" it under the terms of the GNU General Public License as published by
.\" the Free Software Foundation; either version 3 of the License, or
.\" (at your option) any later version.
.\"
.\" This program is distributed in the hope that it will be useful,
.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
.\" GNU General Public License for more details.
.\"
.\" You should have received a copy of the GNU General Public License
.\" along with this program. If not, see
.\" <http://www.gnu.org/licenses/>.
.\"
.ds n) srec_os65a
.TH \*(n) 5 SRecord "Reference Manual"
.SH NAME
srec_os65a \- Ohio Scientific 65A monitor load format
.if require_index \{
.XX "srec_os65a(5)" "Ohio Scientific 65A monitor load format"
.\}
.SH DESCRIPTION
This format is used by the Ohio Scientific 65A monitor. This
family of machines includes the Superboard II, C2, C4, C8,
and Challenger III, when operated with a serial console.
.PP
The file and each subsequent data block starts with a \[lq]R\[rq] to ensure
command mode.
A load operation is initiated with a \[lq]L\[rq], then a 4\[hy]digit hex
address.
There is no need for additional address specifications unless there are gaps in
the data file.
.LP
If no address information is provided, data is assumed to start at 0x0000.
To omit the initial address, use \[lq]-enable optional_address\[rq].
It is not advisable to overwrite data in the first 512 bytes of memory space
without extreme care.
.LP
Each data byte is represented as two hexadecimal characters.
The 65A monitor ignores all non-hexadecimal or command characters, but default
formatting is 32 bytes per line, with a carriage return, line feed combination
(0x0D, 0x0A) at the end of line. This provides clean formatting when pasting
into a terminal.
.LP
Data loading is concluded with a \[lq]R\[rq] to return to command mode.
If an address to start execution is specified, then it is entered into addresses
0x012E and 0x012F, followed by a \[lq]R\[rq] to return to command mode, and a
\[lq]G\[rq] to transfer execution to the specified address.
.SS Size Multiplier
In general, binary data will expand in sized by approximately 2.3 times
when represented with this format.
.\" ------------------------------------------------------------------------
.br
.ne 2i
.SH EXAMPLE
Here is an example Ohio Scientific 65A monitor load file.
It contains the data \[lq]Hello, World\[rq] to be loaded at address 0x1000,
with execution at 0x1010.
.LP
RL1000
.br
48656C6C6F2C20576F726C64
.br
RL012E1010RG
.LP
When loading into the 65A monitor, \[lq]R\[rq] will not be shown, and
will be replaced with a carriage return, line feed combination.
.RE
.\" ------------------------------------------------------------------------
.ds n) srec_cat
.so man1/z_copyright.so
3 changes: 2 additions & 1 deletion srecord/arglex/tool.cc
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,8 @@ srecord::arglex_tool::arglex_tool(int argc, char **argv) :
{ "-Not_INCLude", token_include_not, },
{ "-OFfset", token_offset, },
{ "-Ohio_Scientific", token_ohio_scientific, },
{ "-Ohio_Scientific65v", token_ohio_scientific, },
{ "-Ohio_Scientific65A", token_os65a, },
{ "-Ohio_Scientific65V", token_ohio_scientific, },
{ "-OR", token_or, },
{ "-Output", token_output, },
{ "-Output_Words", token_output_word, },
Expand Down
1 change: 1 addition & 0 deletions srecord/arglex/tool.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ class arglex_tool:
token_offset,
token_ohio_scientific,
token_or,
token_os65a,
token_output,
token_output_word,
token_over,
Expand Down
6 changes: 6 additions & 0 deletions srecord/arglex/tool/input.cc
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include <srecord/input/file/motorola.h>
#include <srecord/input/file/msbin.h>
#include <srecord/input/file/needham.h>
#include <srecord/input/file/os65a.h>
#include <srecord/input/file/os65v.h>
#include <srecord/input/file/ppb.h>
#include <srecord/input/file/ppx.h>
Expand Down Expand Up @@ -416,6 +417,11 @@ srecord::arglex_tool::get_simple_input()
ifp = input_file_os65v::create(fn);
break;

case token_os65a:
token_next();
ifp = input_file_os65a::create(fn);
break;

case token_ppx:
token_next();
ifp = input_file_ppx::create(fn);
Expand Down
6 changes: 6 additions & 0 deletions srecord/arglex/tool/output.cc
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
#include <srecord/output/file/motorola.h>
#include <srecord/output/file/msbin.h>
#include <srecord/output/file/needham.h>
#include <srecord/output/file/os65a.h>
#include <srecord/output/file/os65v.h>
#include <srecord/output/file/ppb.h>
#include <srecord/output/file/ppx.h>
Expand Down Expand Up @@ -266,6 +267,11 @@ srecord::arglex_tool::get_output()
ofp = srecord::output_file_os65v::create(fn);
break;

case token_os65a:
token_next();
ofp = srecord::output_file_os65a::create(fn);
break;

case token_ppb:
token_next();
ofp = srecord::output_file_ppb::create(fn);
Expand Down
2 changes: 2 additions & 0 deletions srecord/input/file/guess.cc
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#include <srecord/input/file/motorola.h>
#include <srecord/input/file/msbin.h>
#include <srecord/input/file/needham.h>
#include <srecord/input/file/os65a.h>
#include <srecord/input/file/os65v.h>
#include <srecord/input/file/ppb.h>
#include <srecord/input/file/ppx.h>
Expand Down Expand Up @@ -87,6 +88,7 @@ static func_p table[] =
srecord::input_file_mos_tech::create,
srecord::input_file_motorola::create,
srecord::input_file_needham::create,
srecord::input_file_os65a::create,
srecord::input_file_os65v::create,
srecord::input_file_ppb::create,
srecord::input_file_ppx::create,
Expand Down
198 changes: 198 additions & 0 deletions srecord/input/file/os65a.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
/**
* srecord - manipulate eprom load files
* Copyright (C) 2002, 2006-2010 Peter Miller
*
* Ohio Scientific 65A input is:
*
* Copyright (c) 2023 Glitch Works, LLC
* http://www.glitchwrks.com/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <srecord/arglex/tool.h>
#include <srecord/input/file/os65a.h>
#include <srecord/record.h>


srecord::input_file_os65a::input_file_os65a(const std::string &a_file_name) :
srecord::input_file(a_file_name),
address(0),
go_address_high(0),
go_address_low(0),
go_address_high_loaded(false),
go_address_low_loaded(false),
state(state_null)
{
}


srecord::input_file::pointer
srecord::input_file_os65a::create(const std::string &a_file_name)
{
return pointer(new srecord::input_file_os65a(a_file_name));
}


bool
srecord::input_file_os65a::read_inner(srecord::record &record)
{
for (;;)
{
if (ignore_the_rest)
return false;

int c = get_char();
switch (c)
{

// Return command, back to command state
case 'R':
state = state_command;
continue;

// Load state, next two bytes are address, following are data
case 'L':
state = state_load_address;
address = get_word_be();
state = state_load_data;

if (address == 0x012E)
{
// Loading address 0x012E is a special case, it is the value
// placed in the high byte of the program counter (PC) when the
// GO command is executed.
state = state_pch_loaded;
go_address_high = get_byte();
go_address_high_loaded = true;
}

if (address == 0x012F)
{
// This would be an unusual case where the PC is loaded in two
// different chunks. Not likely, but permissible.
go_address_low = get_byte();
go_address_low_loaded = true;
warning("PC value written in two separate load commands");
}

continue;

// Go command, start executing a program
case 'G':
state = state_go;

if (!go_address_high_loaded || !go_address_low_loaded)
{
warning("GO command found, but PC not initialized!");
}

address = (go_address_high << 8) | go_address_low;

record =
srecord::record
(
srecord::record::type_execution_start_address,
address,
0,
0
);

ignore_the_rest = true;
return true;

// End-of-file
case -1:
return false;

// Eat whitespace
case '\r':
case '\n':
case ' ':
continue;

// None of the state changes or whitespace, so it's either data or
// an error.
default:
if (state == state_pch_loaded)
{
// We've got the PC high address byte, and if we're here the
// next thing is the low address byte.
get_char_undo(c);
go_address_low = get_byte();
go_address_low_loaded = true;
state = state_load_data;
continue;
}

if (state == state_load_data)
{
// At this point, we know we've gotten the first letter of a
// byte in hex.
get_char_undo(c);

uint8_t buf[1];
buf[0] = get_byte();
record =
srecord::record
(
srecord::record::type_data,
address++,
buf,
1
);

return true;
}

if (state == state_null)
{
// Not in a known state, so there must be something non-65A in
// the input file.
fatal_error("unknown command");
return false;
}
}
}
}


bool
srecord::input_file_os65a::read(srecord::record &record)
{
if (!read_inner(record))
{
if (!seen_some_input)
fatal_error("file contains no data");
return false;
}
seen_some_input = true;
return true;
}


const char *
srecord::input_file_os65a::get_file_format_name()
const
{
return "Ohio Scientific 65A";
}


int
srecord::input_file_os65a::format_option_number()
const
{
return arglex_tool::token_os65a;
}
Loading

0 comments on commit 401ad21

Please sign in to comment.