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
|
|
]
|
|
|
|
{-----------------------------------------------------------------------+
|
|
| |
|
|
+-----------------------------------------------------------------------}
|
|
|