gigatron/rom/Apps/CardBoot/old/System.gcl
2025-01-28 19:17:01 +03:00

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