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:
parent
00be0b552e
commit
24ae52e3cc
@ -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.
|
||||
|
@ -1,3 +1,4 @@
|
||||
;DON'T USE THIS
|
||||
;a single-load PC-88 program
|
||||
[compilation]
|
||||
arch=z80
|
||||
|
20
include/zxspectrum.ini
Normal file
20
include/zxspectrum.ini
Normal 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
|
||||
|
||||
|
@ -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))
|
||||
|
91
src/main/scala/millfork/output/TapOutput.scala
Normal file
91
src/main/scala/millfork/output/TapOutput.scala
Normal 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
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user