1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-12-29 02:31:45 +00:00

Really early and very incomplete ZX Spectrum support

This commit is contained in:
Karol Stasiak 2018-07-03 22:37:18 +02:00
parent 00be0b552e
commit 24ae52e3cc
5 changed files with 115 additions and 0 deletions

View File

@ -56,6 +56,8 @@ Read [the Apple 2 programming guide](./apple2-programming-guide.md) for more inf
* `pc88` NEC PC-88 (very incomplete and not usable for anything yet)
* `zxspectrum` Sinclair ZX Spectrum 48k (very incomplete and not usable for anything yet)
The primary and most tested platform is Commodore 64.
Currently, targets that assume that the program will be loaded from disk or tape are better tested.

View File

@ -1,3 +1,4 @@
;DON'T USE THIS
;a single-load PC-88 program
[compilation]
arch=z80

20
include/zxspectrum.ini Normal file
View File

@ -0,0 +1,20 @@
;DON'T USE THIS
;a single-load ZX Spectrum 48k program
[compilation]
arch=z80
modules=default_panic
[allocation]
segments=default,slowram
segment_default_start=$8000
segment_default_datastart=after_code
segment_default_end=$ffff
segment_slowram_start=$5ccb
segment_slowram_end=$7fff
[output]
style=single
format=tap
extension=tap

View File

@ -158,6 +158,7 @@ object Platform {
case "pagecount" => PageCountOutput
case "allocated" => AllocatedDataOutput
case "d88" => D88Output
case "tap" => TapOutput
case n => n.split(":").filter(_.nonEmpty) match {
case Array(b, s, e) => BankFragmentOutput(b, parseNumber(s), parseNumber(e))
case Array(s, e) => CurrentBankFragmentOutput(parseNumber(s), parseNumber(e))

View File

@ -0,0 +1,91 @@
package millfork.output
import java.nio.charset.StandardCharsets
/**
* @author Karol Stasiak
*/
object TapOutput extends OutputPackager {
def isAlphanum(c: Char): Boolean = (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')
override def packageOutput(mem: CompiledMemory, bank: String): Array[Byte] = {
val filteredName: String = mem.programName.filter(isAlphanum)
val b = mem.banks(bank)
val code = b.output.slice(b.start, b.end + 1)
val codeDataBlock = new DataBlock(code)
val codeHeaderBlock = new HeaderBlock(3, "CODE", code.length, b.start, 32768)
val loaderDataBlock = new DataBlock(ZxSpectrumBasic.loader("CODE", filteredName, b.start))
val loaderHeaderBlock = new HeaderBlock(0, "LOADER", loaderDataBlock.inputData.length, 10, loaderDataBlock.inputData.length)
val result = Array(loaderHeaderBlock, loaderDataBlock, codeHeaderBlock, codeDataBlock).map(_.toArray)
result.flatten
}
}
abstract class TapBlock {
def rawData: Array[Byte]
def checksum: Byte = rawData.foldLeft(0)(_ ^ _).toByte
def toArray: Array[Byte] = Array[Byte](
rawData.length.+(1).toByte,
rawData.length.+(1).>>(8).toByte
) ++ rawData :+ checksum
}
class HeaderBlock(typ: Int, name: String, lengthOfData: Int, param1: Int, param2: Int) extends TapBlock {
val rawData: Array[Byte] = Array[Byte](0, typ.toByte) ++ name.take(10).padTo(10, ' ').getBytes(StandardCharsets.US_ASCII) ++ Array[Byte](
lengthOfData.toByte,
lengthOfData.>>(8).toByte,
param1.toByte,
param1.>>(8).toByte,
param2.toByte,
param2.>>(8).toByte,
)
}
class DataBlock(val inputData: Array[Byte]) extends TapBlock {
val rawData: Array[Byte] = 0xff.toByte +: inputData
}
object ZxSpectrumBasic {
class Snippet(val array: Array[Byte]) extends AnyVal
implicit def _implicit_String_to_Snippet(s: String): Snippet = new Snippet(s.getBytes(StandardCharsets.US_ASCII))
private def token(i: Int) = new Snippet(Array(i.toByte))
val SCREEN$: Snippet = token(170)
val CODE: Snippet = token(175)
val VAL: Snippet = token(176)
val USR: Snippet = token(192)
val INK: Snippet = token(217)
val PAPER: Snippet = token(218)
val BORDER: Snippet = token(231)
val REM: Snippet = token(234)
val LOAD: Snippet = token(239)
val POKE: Snippet = token(244)
val PRINT: Snippet = token(245)
val RUN: Snippet = token(247)
val RANDOMIZE: Snippet = token(249)
val CLS: Snippet = token(251)
val CLEAR: Snippet = token(253)
def line(number: Int, tokens: Snippet*): Array[Byte] = {
val content = tokens.flatMap(_.array).toArray
Array[Byte](number.>>(8).toByte, number.toByte, (content.length + 1).toByte, (content.length + 1).>>(8).toByte) ++ content :+ 13.toByte
}
private def quoted(a: Any): Snippet = "\"" + a + "\""
def loader(filename: String, rem: String, loadAddress: Int): Array[Byte] = {
Array(
line(10, REM, rem),
line(20, BORDER, VAL, "\"0\":", INK, VAL, "\"0\":", PAPER, VAL, "\"7\":", CLS),
line(30, CLEAR, VAL, quoted(loadAddress - 1)),
line(40, LOAD, quoted(filename), CODE),
line(50, RANDOMIZE, USR, VAL, quoted(loadAddress))
).flatten
}
}