Skip to content

Commit

Permalink
Implement AtariCasConverter
Browse files Browse the repository at this point in the history
  • Loading branch information
Stefan Schramm committed Dec 19, 2023
1 parent 3b2f12b commit 3df2eb4
Show file tree
Hide file tree
Showing 10 changed files with 104 additions and 20 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
include ../Makefile.inc

all: rl.cas
all: rl.bin rl.cas

# https://atari800.github.io/
run: rl.cas
Expand All @@ -10,11 +10,11 @@ run: rl.cas
retroload $< -o $@

# https://a8cas.sourceforge.net/
%.cas: %.img
%.cas: %.bin
a8cas-convert -f c -r $< $@

%.img: %.asm
%.bin: %.asm
$(65XX_ASM) $< -o $@

clean:
rm -f *.wav *.cas *.img
rm -f *.wav *.cas *.bin
Binary file added retroload-lib/examples/formats/atari_bin/rl.bin
Binary file not shown.
2 changes: 1 addition & 1 deletion retroload-lib/src/Examples.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const examples: ExampleDefinition[] = [
},
// Atari 800 XL
{
dir: 'atari_cas',
dir: 'atari_bin',
file: 'rl.cas',
options: {},
hash: '8d36a2a696c7e27807c4d1f058fdec34',
Expand Down
13 changes: 13 additions & 0 deletions retroload-lib/src/common/Utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@ export function calculateChecksum8Xor(ba: BufferAccess, initial = 0x00) {
return sum;
}

export function calculateChecksum8WithCarry(ba: BufferAccess) {
// 8 bit checksum with carry being added
let sum = 0;
for (let i = 0; i < ba.length(); i++) {
sum += ba.getUint8(i);
if (sum > 255) {
sum = (sum & 0xff) + 1;
}
}

return sum;
}

/**
* https://gist.github.com/chitchcock/5112270?permalink_comment_id=3834064#gistcomment-3834064
*
Expand Down
11 changes: 10 additions & 1 deletion retroload-lib/src/conversion/ConverterManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,20 @@ import {BufferAccess} from '../common/BufferAccess.js';
import {convert} from './ConverterManager.js';
import * as fs from 'fs';

test('ConverterManager calls convert function of ConverterDefinition', () => {
test('Format kctap is converted correctly', () => {
const data = BufferAccess.createFromNodeBuffer(fs.readFileSync(getLocalPathByDirAndFile('kc851_tap', 'rl.com')));

const result = convert(data, 'kctap', {});

const expectedData = BufferAccess.createFromNodeBuffer(fs.readFileSync(getLocalPathByDirAndFile('kc851_tap', 'rl.tap')));
expect(result.asHexDump()).toBe(expectedData.asHexDump());
});

test('Format ataricas is converted correctly', () => {
const data = BufferAccess.createFromNodeBuffer(fs.readFileSync(getLocalPathByDirAndFile('atari_bin', 'rl.bin')));

const result = convert(data, 'ataricas', {});

const expectedData = BufferAccess.createFromNodeBuffer(fs.readFileSync(getLocalPathByDirAndFile('atari_bin', 'rl.cas')));
expect(result.asHexDump()).toBe(expectedData.asHexDump());
});
2 changes: 2 additions & 0 deletions retroload-lib/src/conversion/ConverterProvider.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import {type ConverterDefinition} from './converter/ConverterDefinition.js';
import AtariCasConverter from './converter/AtariCasConverter.js';
import KcTapConverter from './converter/KcTapConverter.js';

export const converters: ConverterDefinition[] = [
AtariCasConverter,
KcTapConverter,
];
71 changes: 71 additions & 0 deletions retroload-lib/src/conversion/converter/AtariCasConverter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import {BufferAccess} from '../../common/BufferAccess.js';
import {calculateChecksum8WithCarry} from '../../common/Utils.js';
import {type OptionContainer} from '../../encoding/Options.js';
import {type ConverterDefinition} from './ConverterDefinition.js';

const definition: ConverterDefinition = {
name: 'Atari .CAS-File',
identifier: 'ataricas',
options: [],
convert,
};
export default definition;

const markerByte = 0x55;
const blockTypeFull = 0xfc;
const blockTypePartial = 0xfa;
const blockTypeEndOfFile = 0xfe;
const dataBytesPerBlock = 128;

const pilotIrgLength = 20000;
const defaultIrgLength = 250; // TODO: longer for 'ENTER'-loading

function convert(data: BufferAccess, _options: OptionContainer): BufferAccess {
const chunks = data.chunks(dataBytesPerBlock);

// FUJI-Header, baud-block, data blocks, end of file block
const outBa = BufferAccess.create(8 + 8 + (chunks.length + 1) * (8 + 132));

outBa.writeAsciiString('FUJI');
outBa.writeUint16Le(0x0000);
outBa.writeUint16Le(0x0000);

outBa.writeAsciiString('baud');
outBa.writeUint16Le(0x0000);
outBa.writeUint16Le(600); // default baud rate

for (let i = 0; i < chunks.length; i++) {
const chunk = chunks[i];

outBa.writeAsciiString('data');
outBa.writeUint16Le(132);
outBa.writeUint16Le(i === 0 ? pilotIrgLength : defaultIrgLength);

const blockBa = BufferAccess.create(132);
blockBa.writeUint8(markerByte);
blockBa.writeUint8(markerByte);
const partialBlock = chunk.length() !== dataBytesPerBlock;
const blockType = partialBlock ? blockTypePartial : blockTypeFull;
blockBa.writeUint8(blockType);
blockBa.writeBa(partialBlock ? chunk.chunksPadded(dataBytesPerBlock, 0x00)[0] : chunk);
if (partialBlock) {
blockBa.setUint8(130, chunk.length());
}
blockBa.setUint8(131, calculateChecksum8WithCarry(blockBa));

outBa.writeBa(blockBa);
}

// end of file block
outBa.writeAsciiString('data');
outBa.writeUint16Le(132);
outBa.writeUint16Le(defaultIrgLength);
const endBlock = BufferAccess.create(132);
endBlock.writeUint8(markerByte);
endBlock.writeUint8(markerByte);
endBlock.writeUint8(blockTypeEndOfFile);
endBlock.setUint8(131, calculateChecksum8WithCarry(endBlock));
outBa.writeBa(endBlock);

return outBa;
}
17 changes: 3 additions & 14 deletions retroload-lib/src/encoding/adapter/atari/AtariGenericAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {type OptionContainer} from '../../Options.js';
import {type RecorderInterface} from '../../recorder/RecorderInterface.js';
import {unidentifiable, type FormatIdentification} from '../AdapterDefinition.js';
import {type AdapterDefinition} from '../AdapterDefinition.js';
import {calculateChecksum8WithCarry} from '../../../common/Utils.js';

const definition: AdapterDefinition = {
name: 'Atari (Generic data)',
Expand Down Expand Up @@ -45,7 +46,7 @@ function encode(recorder: RecorderInterface, ba: BufferAccess, _options: OptionC
blockBa.setUint8(130, chunkBa.length());
}

blockBa.setUint8(131, calculateChecksum(blockBa));
blockBa.setUint8(131, calculateChecksum8WithCarry(blockBa));
e.recordIrg((blockId === 0) ? pilotIrgLength : defaultIrgLength); // TODO: create option (longer values are required for "ENTER-loading")
e.recordBytes(blockBa);
}
Expand All @@ -55,20 +56,8 @@ function encode(recorder: RecorderInterface, ba: BufferAccess, _options: OptionC
eofBlockBa.writeUint8(markerByte);
eofBlockBa.writeUint8(markerByte);
eofBlockBa.writeUint8(blockTypeEndOfFile);
eofBlockBa.setUint8(131, calculateChecksum(eofBlockBa));
eofBlockBa.setUint8(131, calculateChecksum8WithCarry(eofBlockBa));
e.recordIrg(defaultIrgLength); // TODO: create option (longer values are required for "ENTER-loading")
e.recordBytes(eofBlockBa);
}

function calculateChecksum(ba: BufferAccess) {
// 8 bit checksum with carry being added
let sum = 0;
for (let i = 0; i < ba.length(); i++) {
sum += ba.getUint8(i);
if (sum > 255) {
sum = (sum & 0xff) + 1;
}
}

return sum;
}

0 comments on commit 3df2eb4

Please sign in to comment.