734 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			734 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|                                                                       |
 | 
						|
|       GTOS                                                            |
 | 
						|
|                                                                       |
 | 
						|
|       !!! Work in progress !!!                                        |
 | 
						|
|                                                                       |
 | 
						|
|       Goal is to provide basic operating system services              |
 | 
						|
|       - Terminal                                                      |
 | 
						|
|       - Card access                                                   |
 | 
						|
|       - Extensibility                                                 |
 | 
						|
|                                                                       |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
gcl0x
 | 
						|
 | 
						|
{
 | 
						|
  XXX  API and registers
 | 
						|
 | 
						|
        +-------+       +--//--+-----//------+-------+-------+
 | 
						|
        | GTOS  |       |      | r0, r1, ... |vIRQ_PC|vIRQ_AC|
 | 
						|
        +-------+       +--//--+-----//------+-------+-------+
 | 
						|
           vLR          ^      ^                             ^
 | 
						|
                        vSP    $e0                        $100
 | 
						|
 | 
						|
        .L2:    LD    vLR
 | 
						|
                STW   MyGtos
 | 
						|
                ...
 | 
						|
                LDI   0
 | 
						|
                CALL  MyGtos
 | 
						|
 | 
						|
        Path/command sequences:
 | 
						|
                <name
 | 
						|
                >name
 | 
						|
                0<name
 | 
						|
                0>name
 | 
						|
                <0:name
 | 
						|
                >0:name
 | 
						|
                >>0:name
 | 
						|
                A:name
 | 
						|
                A:name:command
 | 
						|
 | 
						|
        --------------------------
 | 
						|
        A>type file.txt
 | 
						|
        hello gigatron
 | 
						|
        A>_
 | 
						|
        --------------------------
 | 
						|
 | 
						|
        Function codes:
 | 
						|
                0. Warm restart and reload Command.gt1
 | 
						|
                1. Read single console input with echo
 | 
						|
                2. Console output
 | 
						|
                3. X Read from reader
 | 
						|
                4. X Write to punch
 | 
						|
                5. X Write to list device
 | 
						|
                6. Read console input without echo
 | 
						|
                10. Read line buffer
 | 
						|
                11. Console ready?
 | 
						|
                12. Read list status
 | 
						|
                30. Set list echo status
 | 
						|
                31. Read list echo status
 | 
						|
 | 
						|
                Send char to console
 | 
						|
                Read char from line buffer
 | 
						|
                Open directory
 | 
						|
                Walk through directory
 | 
						|
                Open (after lookup)
 | 
						|
                Read char from file
 | 
						|
 | 
						|
        Logical devices
 | 
						|
                CON     Console (in/out)
 | 
						|
                RDR     Paper tape reader device
 | 
						|
                PUN     Paper tape punch device
 | 
						|
                LST     List output device
 | 
						|
        Physical devices
 | 
						|
                TTY     Teletype
 | 
						|
                CRT     Screen
 | 
						|
                BAT     Batch processing (??)
 | 
						|
                UC1     User-defined console
 | 
						|
                PTR     Paper tape reader (high speed reader)
 | 
						|
                UR1
 | 
						|
                UR2
 | 
						|
                PTP     Paper tape punch
 | 
						|
                LPT     Line printer
 | 
						|
 | 
						|
        Function set in QIODevice
 | 
						|
                https://doc.qt.io/archives/qt-4.8/qiodevice.html
 | 
						|
 | 
						|
  XXX Positive card detection:
 | 
						|
        Read MISO's
 | 
						|
        Invert
 | 
						|
        Write to ZP
 | 
						|
        Read MISO
 | 
						|
  XXX  https://cdn.hackaday.io/images/1744261554112919559.png
 | 
						|
  XXX  SYS extension for copying to and from object variables
 | 
						|
  XXX  LoadtGt1: proper closing of last sector
 | 
						|
  XXX  Compliancy: Reconsider pull-down resistors on MISO lines
 | 
						|
                   Some cards [30%?] [MMC?] have MISO in open collector during
 | 
						|
                   init" But if this is only during CMD0, we can live with it...
 | 
						|
  XXX  Compliancy: Follow cluster chain for directories as well
 | 
						|
  XXX  Speed: Transfer 256 bytes at once
 | 
						|
  XXX  Think about card detect and monitoring it (inserted = low)
 | 
						|
       Put we can always use the MOSI line for this, or check the
 | 
						|
       init status when opening a file
 | 
						|
  XXX  Figure out why Apple formats card with type 0x0b
 | 
						|
  XXX  Check CardType, reject byte addressing
 | 
						|
}
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|                                                                       |
 | 
						|
|       Variables                                                       |
 | 
						|
|                                                                       |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
 | 
						|
zpReset=$c0                     {Start GCL vars from $c0 instead of $30}
 | 
						|
 | 
						|
_TermOut=$300
 | 
						|
_OpenFile=$400
 | 
						|
_Buffer=$500                    {General purpose 512-byte buffer}
 | 
						|
_ClusterList=$7fa0              {Room for 96/4 = 24 clusters}
 | 
						|
 | 
						|
{
 | 
						|
        XXX These must move into an object
 | 
						|
 | 
						|
        Registers
 | 
						|
        ---------
 | 
						|
        ValueL,H        XXXX    32-bit accumulator
 | 
						|
        OffsetL,H       XXXX    Offset to be added to ValueL,H
 | 
						|
 | 
						|
 | 
						|
        Sector reader
 | 
						|
        =============
 | 
						|
 | 
						|
        Variable        Bytes   Description
 | 
						|
        --------        -----   -----------
 | 
						|
        ClusterBaseL,H  ----    Cluster origin (hypothetical cluster 0)
 | 
						|
        SectorL,H       XXXX    Block number of last sector
 | 
						|
        CurrentDirL,H   ----    First cluster of current directory
 | 
						|
        ClusterSize     X       Sectors per cluster: 1, 2, ... 128
 | 
						|
        ClusterMask     X       XXX ClusterSize - 1
 | 
						|
 | 
						|
        File reader
 | 
						|
        ===========
 | 
						|
 | 
						|
        Variable        Bytes   Description
 | 
						|
        --------        -----   -----------
 | 
						|
        FatBaseL,H      ----    XXX
 | 
						|
        FileSizeL,H     ????    File size from directory entry
 | 
						|
        FilePosL,H      --XX    Current R/W position in file: 0..FileSize
 | 
						|
}
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|                       RAM page 2                                      |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
 | 
						|
[
 | 
						|
  \romType, \romTypeValue_DEVROM-
 | 
						|
  [if<0                         {Version mismatch}
 | 
						|
    \CharOut _r0=               {Give error using just ROM v1 features}
 | 
						|
    [def push _r0!] _r1=
 | 
						|
    [def #10 `Need`DEVROM #0] _r1!
 | 
						|
  ]
 | 
						|
 | 
						|
  \TermOut _dev1=               {Closure uses CALLI_DEVROM}
 | 
						|
  [def #10 `GTOS`DEV`32K #0] _dev1!
 | 
						|
 | 
						|
  {
 | 
						|
     Reads MBR and finds the primary partition's start block
 | 
						|
     Check if it is FAT32
 | 
						|
  }
 | 
						|
 | 
						|
  0 SectorL= SectorH=           {First sector on device}
 | 
						|
  \ReadSector!                  {Read MBR}
 | 
						|
 | 
						|
  $6fe deek k=                  {Fetch signature}
 | 
						|
  $aa55 k^                      {Check signature}
 | 
						|
 | 
						|
  [if=0
 | 
						|
    $6c6 deek SectorL=          {Primary partition's first sector on disk}
 | 
						|
    $6c8 deek SectorH=
 | 
						|
 | 
						|
    $6c2 peek k=                {Filesystem type code}
 | 
						|
    $0b k^ [if<>0 $07^]         {Accepts $0b and $0c}
 | 
						|
                                {XXX In case of $0b: check for CHS vs LBA ???}
 | 
						|
  ]
 | 
						|
 | 
						|
  {
 | 
						|
    Reads the first block of the partition. Mind that, despite it's name,
 | 
						|
    this block doesn't hold the volume label: that is stored in the root
 | 
						|
    directory instead.
 | 
						|
  }
 | 
						|
 | 
						|
  \ReadSector!                  {SectorL,H as set by ReadMBR}
 | 
						|
 | 
						|
  $50b deek k=                  {Confirm expected sector length}
 | 
						|
  $200 k^                       {Only $200 is ok}
 | 
						|
 | 
						|
  {
 | 
						|
    Calculate cluster base and set current directory to root
 | 
						|
 | 
						|
        ClusterBaseL,H =
 | 
						|
                PartitionL,H                    from MBR
 | 
						|
              + Reserved Sectors                from VolumeId
 | 
						|
              + (Number of FATs * FAT size)     from VolumeId
 | 
						|
              - 2 * ClusterSize                 from VolumeId
 | 
						|
  }
 | 
						|
 | 
						|
  $50d peek                     {Sectors per cluster}
 | 
						|
  ClusterSize=
 | 
						|
 | 
						|
  SectorL ValueL=               {Partition's first sector, from MBR}
 | 
						|
  SectorH ValueH=
 | 
						|
 | 
						|
  $50e deek OffsetL=            {Number of reserved sectors}
 | 
						|
  0 OffsetH=
 | 
						|
  \AddOffset!
 | 
						|
 | 
						|
  FatBaseL=
 | 
						|
  ValueH FatBaseH=              {Begin of primary FAT area}
 | 
						|
 | 
						|
  $524 deek OffsetL=            {FAT size in sectors}
 | 
						|
  $526 deek OffsetH=
 | 
						|
  \AddOffset! \AddOffset!       {Number of FATs is always 2}
 | 
						|
 | 
						|
  0 ClusterSize- ClusterSize-   {Subtract twice to arrive at ClusterBase}
 | 
						|
  OffsetL=
 | 
						|
  $ffff OffsetH=
 | 
						|
  \AddOffset!
 | 
						|
 | 
						|
         ClusterBaseL=
 | 
						|
  ValueH ClusterBaseH=
 | 
						|
 | 
						|
  { First cluster of root directory }
 | 
						|
  $52c deek CurrentDirL=
 | 
						|
  $52e deek CurrentDirH=
 | 
						|
 | 
						|
  \ReadDirectory!               {Read root directory}
 | 
						|
  [do loop]
 | 
						|
]
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|                       RAM page 3                                      |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
*=$300
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|                                                                       |
 | 
						|
|       Video terminal section                                          |
 | 
						|
|                                                                       |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
 | 
						|
{ Closure for writing to video terminal }
 | 
						|
_TermOut=*
 | 
						|
[
 | 
						|
  { Function }
 | 
						|
  push \CharOut!
 | 
						|
  { Variables }
 | 
						|
  ##$7802                       {Pos}
 | 
						|
]
 | 
						|
 | 
						|
{ CharOut -- writes _r1 _sysFn _sysArgs[01245] }
 | 
						|
 | 
						|
_CharOut=*
 | 
						|
[
 | 
						|
  _r0=
 | 
						|
  [do _r0, if<>0
 | 
						|
 | 
						|
  10^ [if<>0                    {Test for CR}
 | 
						|
    _vLR, 155- if>0 0]          { or line wrap}
 | 
						|
  [if=0
 | 
						|
 | 
						|
    { Clear new line first }
 | 
						|
    \sysArgs2.                  {Set all-zero output pattern}
 | 
						|
    $20 \sysArgs0.              {Blue}
 | 
						|
    $e0+ _r1=                   {Video table}
 | 
						|
    255+ deek 255| 255^         {Go to what is now still the top text row}
 | 
						|
    _sysArgs4=                  {Start of line for clearing}
 | 
						|
    2+ _vLR:                    {Indent by 2 pixels for text}
 | 
						|
    \SYS_VDrawBits_134 _sysFn=  {SYS call to draw 8 pixels vertically}
 | 
						|
    [do
 | 
						|
      134!!
 | 
						|
      <_sysArgs4++              {Step 1 pixel right}
 | 
						|
      \sysArgs4, 160^           {Test for end of line}
 | 
						|
      if<>0loop]
 | 
						|
 | 
						|
    { Then scroll up by modifying videoTable }
 | 
						|
    [do
 | 
						|
      _r1 16+ _r2=              {_r2 looks 8 entries ahead of _r1}
 | 
						|
      peek \sysArgs0.           {Swap scanlines}
 | 
						|
      _r1, _r2.
 | 
						|
      \sysArgs0, _r1.
 | 
						|
      <_r1++ <_r1++             {Advance to next entry in video table}
 | 
						|
      <_r1, 224^ if<>0loop]     {Until all done}
 | 
						|
  ]
 | 
						|
 | 
						|
  { Draw ASCII character (>=32) on screen using the 5x8 pixel built-in font }
 | 
						|
  _r0, 32-
 | 
						|
 | 
						|
{ XXX TODO
 | 
						|
  32- [if<0 127 else 96-        {Map any non-ASCII to block symbol 127}
 | 
						|
       if>=0 127] 127& }
 | 
						|
 | 
						|
  [if>=0
 | 
						|
    50-                         {Map ASCII code to offset in font table}
 | 
						|
    [if<0 50+ _r1= &_font32up   {First page for ASCII 32..81}
 | 
						|
     else _r1= &_font82up] _r2= {Second page is ASCII 82..127}
 | 
						|
    _r1 2<< _r1+                {Multiply by 5}
 | 
						|
    _r2+ _r2=                   {Add page address to reach bitmap data}
 | 
						|
    $3f20 _sysArgs0=            {White on blue}
 | 
						|
    _vLR; _sysArgs4=            {Position of character}
 | 
						|
    6+ _vLR:                    {Advance position by 6 pixels for next call}
 | 
						|
    \SYS_VDrawBits_134 _sysFn=  {Prepare SYS calls}
 | 
						|
    5 [do _r1=                  {Loop to draw 5 vertical slices of 8 pixels}
 | 
						|
      _r2 0?? \sysArgs2.        {Get byte from ROM using `LUP 0' instruction}
 | 
						|
      134!!                     {Invoke SYS function to draw 8 vertical pixels}
 | 
						|
      <_r2++ <_sysArgs4++       {Advance to next slice in ROM and on screen}
 | 
						|
      _r1 1- if>0loop]          {Looping}
 | 
						|
  ]
 | 
						|
 | 
						|
  <_r0++ loop]
 | 
						|
 | 
						|
  pop ret                       {Closure return}
 | 
						|
]
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|                       RAM page 4                                      |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
*=$400
 | 
						|
 | 
						|
_OpenFile=*
 | 
						|
[
 | 
						|
  {
 | 
						|
    Prepare for reading file whose directory entry is pointed at by p
 | 
						|
  }
 | 
						|
  push
 | 
						|
  0 FilePosL= FilePosH=         {Reset position in file}
 | 
						|
  p $1c+ deek FileSizeL=        {Length of file in bytes}
 | 
						|
  p $1e+ deek FileSizeH=
 | 
						|
  p $1a+ deek ValueL=           {First cluster for file}
 | 
						|
  p $14+ deek ValueH=
 | 
						|
{
 | 
						|
  \ReadClusterChain!
 | 
						|
}
 | 
						|
  \ClusterToSector!
 | 
						|
  pop ret
 | 
						|
]
 | 
						|
 | 
						|
_ReadSector=*
 | 
						|
[
 | 
						|
  {
 | 
						|
    Read sector from card into memory (clobbers ValueL,H and OffsetL,H)
 | 
						|
  }
 | 
						|
  push
 | 
						|
 | 
						|
  \SYS_ExpanderControl_v4_40    {SYS function}
 | 
						|
  _sysFn= $8078 40!!            {Enable SPI0, keep MOSI high, bank=1}
 | 
						|
 | 
						|
  {
 | 
						|
    CMD17 READ_SINGLE_BLOCK
 | 
						|
    Reads a block of the size selected by the SET_BLOCKLEN command
 | 
						|
  }
 | 
						|
 | 
						|
  [def #$51 #0 #0 #0 #0 #0]     {CMD17}
 | 
						|
  p= q=
 | 
						|
  >SectorH, <q++ q.             {Put SectorL,H in argument, big-endian order}
 | 
						|
  <SectorH, <q++ q.
 | 
						|
  >SectorL, <q++ q.
 | 
						|
  <SectorL, <q++ q.
 | 
						|
  p \SendCommandToCard!
 | 
						|
 | 
						|
  \WaitForCardReply!
 | 
						|
  254&                          {Only 0 and 1 mean success}
 | 
						|
 | 
						|
  [if=0
 | 
						|
    {Wait for first data byte}
 | 
						|
    [do
 | 
						|
      \SendOnesToCard!          {XXX Can we use WaitForCardReply here?}
 | 
						|
      $ff^ if=0loop]            {XXX Loop needs a timeout}
 | 
						|
      {\sysArgs6, $fe^          {Only $fe is OK}}
 | 
						|
  ]
 | 
						|
 | 
						|
  \Buffer q=                    {Setup write pointer}
 | 
						|
  512 [do k=                    {Number of bytes to read}
 | 
						|
    \SendOnesToCard!            {XXX Read directly into buffer}
 | 
						|
    q.                          {Store byte in buffer}
 | 
						|
    q 1+ q=                     {Advance write pointer, cross page boundaries}
 | 
						|
    k 1- if>0loop]              {Looping}
 | 
						|
 | 
						|
  \SendOnesToCard!              {Read 16-bit checksum}
 | 
						|
  \SendOnesToCard!
 | 
						|
 | 
						|
  \SYS_ExpanderControl_v4_40    {SYS function}
 | 
						|
  _sysFn= $807c 40!!            {Disable SPI0, keep MOSI high, bank=1}
 | 
						|
 | 
						|
  pop ret
 | 
						|
]
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|                                                                       |
 | 
						|
|       Memory card section                                             |
 | 
						|
|                                                                       |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|                       RAM page $7f                                    |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
*=$7fa0
 | 
						|
 | 
						|
{
 | 
						|
        Bus     ROM v4+
 | 
						|
        ---     --------
 | 
						|
        A0      SCLK
 | 
						|
        A1      (unused)
 | 
						|
        A2      /SS0
 | 
						|
        A3      /SS1
 | 
						|
        A4      /SS2
 | 
						|
        A5      /SS3
 | 
						|
        A6      B0
 | 
						|
        A7      B1
 | 
						|
        A8-A14  (unused)
 | 
						|
        A15     MOSI
 | 
						|
}
 | 
						|
 | 
						|
_SendOnesToCard=*
 | 
						|
[
 | 
						|
  255 \sysArgs6.                {Place byte in exchange buffer}
 | 
						|
  \sysArgs6 _sysArgs0=          {Begin}
 | 
						|
  1+        _sysArgs2=          {End}
 | 
						|
  \SYS_SpiExchangeBytes_v4_134  {SYS function}
 | 
						|
  _sysFn= 134!!                 {Exchanges a single byte}
 | 
						|
  \sysArgs6,                    {Reply byte}
 | 
						|
  ret
 | 
						|
]
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|                       RAM page $7e                                    |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
*=$7ea0
 | 
						|
 | 
						|
_SendCommandToCard=*
 | 
						|
[
 | 
						|
  push
 | 
						|
 | 
						|
  { Setup command bytes }
 | 
						|
  p= \Buffer q=
 | 
						|
  255 q. <q++ q. <q++           {Start with two dummy bytes}
 | 
						|
  6 [do i=                      {Copy 6 command bytes to exchange buffer}
 | 
						|
    p, <p++                     {Fetch byte and advance read pointer}
 | 
						|
    q. <q++                     {Store byte and advance write pointer}
 | 
						|
    i 1- if>0loop]              {Looping}
 | 
						|
 | 
						|
  { Send to SPI device }
 | 
						|
  \Buffer _sysArgs0=            {Begin}
 | 
						|
  8+      _sysArgs2=            {End and overwrite exchange buffer}
 | 
						|
  \SYS_SpiExchangeBytes_v4_134  {SYS function}
 | 
						|
  _sysFn= 134!!
 | 
						|
 | 
						|
  pop ret
 | 
						|
]
 | 
						|
 | 
						|
_WaitForCardReply=*
 | 
						|
[
 | 
						|
  push
 | 
						|
  {
 | 
						|
  |  """To receive this message, your program should continuously toggle
 | 
						|
  |     the SD CLK signal and observe the MISO line for data, while keeping
 | 
						|
  |     the MOSI line high and the CS line low. Your program can detect the
 | 
						|
  |     message, because every message begins with a 0 bit, and when the
 | 
						|
  |     SD card sends no data it keeps the MISO line high."""
 | 
						|
  |
 | 
						|
  |  """Note that the response to each command is sent by the card a few
 | 
						|
  |     SD CLK cycles later. If the expected response is not received within
 | 
						|
  |     16 clock cycles after sending the reset command, the reset command
 | 
						|
  |     has to be sent again."""
 | 
						|
  }
 | 
						|
  8 [do i=                      {Poll for upto 8 reply bytes}
 | 
						|
    \SendOnesToCard!            {Keep MOSI line high by only sending ones}
 | 
						|
    128&                        {Note: communication is byte-aligned}
 | 
						|
    if<>0                       {Break out when valid message detected}
 | 
						|
      i 1- if>0loop]            {Or when loop counter exhausted}
 | 
						|
 | 
						|
  \sysArgs6,                    {Return reply from card}
 | 
						|
  pop ret
 | 
						|
]
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|                                                                       |
 | 
						|
|       FAT32 section                                                   |
 | 
						|
|                                                                       |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|                       RAM page $7d                                    |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
*=$7da0
 | 
						|
 | 
						|
_ReadDirectory=*
 | 
						|
[
 | 
						|
  {
 | 
						|
    Read first sector of current directory
 | 
						|
  }
 | 
						|
  push
 | 
						|
 | 
						|
  { Sector = ClusterBase + CurrentDir * ClusterSize }
 | 
						|
  CurrentDirL ValueL=
 | 
						|
  CurrentDirH ValueH=
 | 
						|
  \ClusterToSector!
 | 
						|
  \ReadSector!
 | 
						|
  <SectorL++                    {XXX Replace with ClusterList}
 | 
						|
 | 
						|
  pop ret
 | 
						|
]
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|                                                                       |
 | 
						|
|       32-bit arithmetic section                                       |
 | 
						|
|                                                                       |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
 | 
						|
_ClusterToSector=*
 | 
						|
[
 | 
						|
  {
 | 
						|
    Multiply ValueL,H by ClusterSize and add ClusterBase (clobbers OffsetL,H)
 | 
						|
  }
 | 
						|
  push
 | 
						|
  >ValueH, 15& >ValueH.         {Clear the top 4 reserved bits}
 | 
						|
  1 [do k=
 | 
						|
    ClusterSize- if<0
 | 
						|
    \ShiftLeft!
 | 
						|
    k k+ loop]
 | 
						|
 | 
						|
  ClusterBaseL OffsetL=
 | 
						|
  ClusterBaseH OffsetH=
 | 
						|
  \AddOffset!
 | 
						|
 | 
						|
  ValueL SectorL=               {Set as next sector to read}
 | 
						|
  ValueH SectorH=
 | 
						|
 | 
						|
  pop ret
 | 
						|
]
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|                       RAM page $7c                                    |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
*=$7ca0
 | 
						|
 | 
						|
_ShiftLeft=*
 | 
						|
[
 | 
						|
  {
 | 
						|
    Shift left ValueL,H by 1 bit (clobbers OffsetL,H)
 | 
						|
  }
 | 
						|
  push
 | 
						|
  ValueL OffsetL=               {Double value}
 | 
						|
  ValueH OffsetH=
 | 
						|
  \AddOffset!
 | 
						|
  OffsetH                       {Return old high word}
 | 
						|
  pop ret
 | 
						|
]
 | 
						|
 | 
						|
_AddOffset=*
 | 
						|
[
 | 
						|
  {
 | 
						|
    Add 32-bit OffsetL,H to 32-bit ValueL,H and store result there
 | 
						|
  }
 | 
						|
  ValueL OffsetL^ [if<0         {Compare lower halves' most significant bits}
 | 
						|
    ValueL                      {MSB unequal: carry taken from their sum}
 | 
						|
  else
 | 
						|
    $8000                       {MSB equal: carry taken from either term}
 | 
						|
  ] OffsetL+                    {Carry now in MSB of vAC (inverted)}
 | 
						|
  [if>=0 ValueH 1+ ValueH=]     {Apply carry to upper half}
 | 
						|
  ValueH OffsetH+ ValueH=       {Sum upper half}
 | 
						|
  ValueL OffsetL+ ValueL=       {Sum lower half and return this as well}
 | 
						|
  ret
 | 
						|
]
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|                                                                       |
 | 
						|
|       File reading section                                            |
 | 
						|
|                                                                       |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|                       RAM page $7b                                    |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
*=$7ba0
 | 
						|
 | 
						|
{*** some FAT functions ***}
 | 
						|
 | 
						|
_NextSector=*
 | 
						|
[def
 | 
						|
  push
 | 
						|
 | 
						|
  <SectorL++                    {To next sector}
 | 
						|
{
 | 
						|
  $1fff FileSizeL&              {XXX Hardcoded. Should derive from ClusterSize}
 | 
						|
  [if=0
 | 
						|
    List 4+ List= deek ValueL=  {Get next cluster from ClusterList}
 | 
						|
    List 2+       deek ValueH=
 | 
						|
    \ClusterToSector!
 | 
						|
  ]
 | 
						|
}
 | 
						|
  pop ret
 | 
						|
]
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|                       RAM page $7a                                    |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
*=$7aa0
 | 
						|
 | 
						|
_ReadClusterChain=*
 | 
						|
[def
 | 
						|
  {
 | 
						|
    Traverse the FAT and collect the clusters used by this file
 | 
						|
    XXX TODO Don't do this if the file is smaller than the cluster size
 | 
						|
  }
 | 
						|
  push
 | 
						|
 | 
						|
  \ClusterList List=            {Reset}
 | 
						|
  [do
 | 
						|
    { Store in list }
 | 
						|
    ValueL List: <List++ <List++
 | 
						|
    ValueH List: <List++ <List++
 | 
						|
 | 
						|
    { Break at End Of Cluster marker }
 | 
						|
    $f000 ValueH| 1+ [if=0      {Test high word first, ignore bit 28:31}
 | 
						|
    $0007 ValueL| 1+] if<>0     {Optionally test low word, ignore bit 0:2}
 | 
						|
 | 
						|
    \NextCluster!
 | 
						|
    loop]
 | 
						|
 | 
						|
  \ClusterList                  {Reset}
 | 
						|
  List=   deek ValueL=
 | 
						|
  List 2+ deek ValueH=
 | 
						|
 | 
						|
  pop ret
 | 
						|
]
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|                       RAM page $79                                    |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
*=$79a0
 | 
						|
 | 
						|
_NextCluster=*
 | 
						|
[
 | 
						|
  {
 | 
						|
    Find next cluster in FAT
 | 
						|
  }
 | 
						|
  push
 | 
						|
 | 
						|
  >ValueL, <OffsetL.            {Offset = Cluster >> 8}
 | 
						|
  <ValueH, >OffsetL.
 | 
						|
  >ValueH,  OffsetH=
 | 
						|
 | 
						|
  <ValueL, m=                   {Park the lowest 8 bits in m}
 | 
						|
  128& peek ValueL=             {Value = 1 if bit7 else 0}
 | 
						|
  0         ValueH=
 | 
						|
  \AddOffset! \AddOffset!       {Value += 2 * Offset: now we have Cluster >> 7}
 | 
						|
 | 
						|
  FatBaseL OffsetL=             {Value += First sector for FAT}
 | 
						|
  FatBaseH OffsetH=
 | 
						|
  \AddOffset!
 | 
						|
 | 
						|
  SectorL^                      {Compare sector number to what we already have}
 | 
						|
  [if=0 ValueH SectorH^]
 | 
						|
 | 
						|
  [if<>0
 | 
						|
    ValueL SectorL=             {Read new sector from FAT area}
 | 
						|
    ValueH SectorH=
 | 
						|
    \ReadSector!
 | 
						|
  ]
 | 
						|
 | 
						|
  m 127& 2<< m=                 {Index in FAT sector at 32-bit word i & $7f}
 | 
						|
  $500 m+ deek ValueL=          {Fetch next cluster number in the chain}
 | 
						|
  $502 m+ deek ValueH=
 | 
						|
 | 
						|
  pop ret
 | 
						|
]
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|                       RAM page $78                                    |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
*=$78a0
 | 
						|
 | 
						|
{
 | 
						|
  Byte reader           vAC             input
 | 
						|
  Uses                  _r0.._r2        variables
 | 
						|
  Execution address     _r0             output
 | 
						|
}
 | 
						|
 | 
						|
_LoadGt1=*
 | 
						|
[
 | 
						|
  {
 | 
						|
    Load GT1 file into RAM and return start address.
 | 
						|
    See Docs/GT1-files.txt for details on GT1
 | 
						|
    XXX Not robust against early EOF
 | 
						|
    XXX Final EOF must be checked by caller
 | 
						|
  }
 | 
						|
  push
 | 
						|
 | 
						|
  _r1=                          {Byte reader}
 | 
						|
 | 
						|
  { Read file contents byte by byte }
 | 
						|
  _r1!                          {Fetch first byte}
 | 
						|
  [do                           {Chunk copy loop}
 | 
						|
         >_r0.                  {High-address comes first}
 | 
						|
    _r1! <_r0.                  {Then the low address}
 | 
						|
    _r1!                        {Byte count (0 means 256)}
 | 
						|
    [do                         {Byte copy loop}
 | 
						|
      <_r2.                     {Implicitly chops counter to 8 bits}
 | 
						|
      _r1! _r0.                 {Poke next byte into memory}
 | 
						|
      <_r0++                    {Advance write pointer in page}
 | 
						|
      <_r2, 1-                  {Decrement counter}
 | 
						|
      if<>0loop]
 | 
						|
    _r1!                        {Go to next block}
 | 
						|
    if<>0loop]
 | 
						|
  _r1! >_r0.                    {Load execution address}
 | 
						|
  _r1! <_r0.
 | 
						|
 | 
						|
  pop ret
 | 
						|
]
 | 
						|
 | 
						|
_ByteLoader=*
 | 
						|
[
 | 
						|
  $700 _r3^ [if=0
 | 
						|
    [do loop]                   {XXX Read next sector and/or cluster}
 | 
						|
  ]
 | 
						|
  _r3, <r3++                    {Next byte from sector buffer}
 | 
						|
  ret
 | 
						|
]
 | 
						|
 | 
						|
{-----------------------------------------------------------------------+
 | 
						|
|                                                                       |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
 |