Jump to content

Photo

File operations in assembly language

pab dsr tms9900 file i/o

14 replies to this topic

#1 retroclouds OFFLINE  

retroclouds

    Stargunner

  • 1,591 posts
  • Location:Germany

Posted Sun Apr 1, 2018 4:30 AM

Hi everybody,

 

I'm looking for some good working examples of doing disk file I/O operations in assembly language. 

Already looked at section 3 DSR (Device Service Routine) / Disk & File Management in the development resources thread.

 

Lots of good stuff there, but seeing some working code definitely would help.

What I want to accomplish is to create, read, update text files and load binary files.

 

Basically I'd like to write a small programmer's editor for running on the TI-99/4a.

For keeping things simple and compatible with as many disk devices as possible I would only focus on level 3 I/O calls.

 

Now here's where things get tricky. I want to use my spectralibrary for most of the stuff.

I learned that scratchpad memory and VDP memory must be setup in a certain way for DSR calls to work successfully.

 

Currently I'm exclusively using scratchpad memory for all my stuff, so will have to reallocate that to somewhere else. 

That should be possible easily enough. Now here are my questions:

 

1. Does a scratchpad memory map exist, with the minimal requirements for calling DSR routines ?

 

2. How do I need to setup VDP memory so that file I/O is possible. I understand that a PAB must always reside in VDP memory.

    But the disk controller DSR also uses VDP memory for work buffers during file I/O. Does the disk controller DSR always loads files from disk to VDP or is it possible to directly load to RAM ?

 

3. Is it possible to do file I/O calls in pure assembly language or do I have to involve GPL in some way?
The documentation being focussed on Monitor, Basic, etc. seems to imply that GPL is always involved. Is that a necessity? 

 

4. What is the easiest way to "interface" with DSR memory setup. I mean would have to switch between my "application workspace" outside of scratch-pad memory, into the "DSR workspace" in scratch-pad memory and return safely.

 

5. Any good examples out there?

 

 

On a sidenote, I remember a discussion with InsaneMultitasker many moons ago where it was mentioned that a file operations document was in the works. My memory is a bit blurry on that, can't recall if this was finished or not. 

 

 

Cheers

Filip



#2 Stuart OFFLINE  

Stuart

    Dragonstomper

  • 750 posts
  • Location:Southampton, UK

Posted Sun Apr 1, 2018 5:05 AM

Did you look at the Ralph Molesworth "Introduction to Assembly Language ..." book (which you have listed in the Development Resources thread)? That's the book that made file I/O 'click' for me, and contains a good working example. Note that there's an error on page 82 - in the block of two lines near the centre of the page, the first line "LI R6,PAB+9" should be "LI R6,PABADR+9". I also have a scribbled note you need to execute those two lines (basically setting >8356) before *each and every* call to DSRLNK.

 

Your question 2 - I believe all data transfers are through VDP RAM, you can't transfer directly from CPU RAM. So for saving a sequential file for example, you would copy a record from CPU RAM to VDP RAM, write the record to disk, transfer the next record from CPU RAM to VDP RAM, write to disk, and so on.

 

Your question 3 - I don't believe GPL is involved (or has to be involved). Everything is done through DSRLNK calls.

 

Here's a fairly simple example from my Internet Browser program, used to open the Favourites file and read it in line-by-line.

**********************************************************************************
*Define PAB for disk access (loading the favourites file) for TI99EA3 and TI99CRT.
**********************************************************************************

BUFADR  EQU  >1C00          VDP RAM address for record buffer.
PABADR  EQU  >1D00          VDP RAM address for PAB.

PAB     BYTE >00            Op-code: Open.
        BYTE >14            File type: Sequential, Input, Variable, Display.
        DATA BUFADR         Buffer address in VDP RAM.
        BYTE 80             Record length.
        BYTE 80             Character count.
        DATA >0000          Relative record number.
        BYTE >00            Cassette screen offset.
        BYTE 16             File descriptor length.
        TEXT 'DSK.BROWSER.FAVS'  File descriptor.

PABSP   BYTE >01,>16        Program name length and program name for sub-program in Disk Controller DSR that
*                           sets the number of file buffers.

READ    BYTE >02            Op-code for file operation.

        BYTE 0              Padding byte to restore even address.

*Set number of disk files supported to 1 ("CALL FILES(1)") to move the disk controller reserved area in VDP RAM
*from >37D7 (where it overlaps with the colour table and sprite generator table) to >3BE3 (where it only overlaps
*with a part of the sprite generator table that we don't use anyway).

ENDCONF LI   R0,PABADR      Copy PAB for sub-program to VDP RAM.
        LI   R1,PABSP
        LI   R2,2
        BLWP @VMBW

        MOV  R0,@>8356      Set up pointer to sub-program name length byte in VDP RAM.

        LI   R0,>0100       Set number of disk files in the byte required by the sub-program.
        MOVB R0,@>834C

        BLWP @DSRLNK        Call the sub-program.
        DATA 10


=================


*Handle display favourites key.

        MOV  @CRCARDB,R12   Switch off serial interface on NanoPEB/RS-232 Card before using disk access.
        SBZ  0

        LI   R0,PABADR      Copy PAB to VDP RAM.
        LI   R1,PAB
        LI   R2,26
        BLWP @VMBW

        LI   R6,PABADR+9    Set up pointer to file descriptor length byte in VDP RAM.
        MOV  R6,@>8356

        BLWP @DSRLNK        Open the file.
        DATA 8

        JNE  MSSP190        Equal bit in status register will be set on DSR error. Jump if no error.

        BLWP @WRTCHRS       Print DSR error message at bottom of screen.
        DATA 42*23
        DATA MSG12
        DATA 42
        BL   @UPDTSCR

        MOV  @CRCARDB,R12   Switch serial interface on NanoPEB/RS-232 Card back on.
        SBO  0

        B    @MSSP00        Loop round and wait for next character from keyboard.

MSSP190 MOVB @READ,R1       Change file op-code to 'Read'.
        LI   R0,PABADR
        BLWP @VSBW

        LI   R3,PGBUFF      Address of page download buffer.

MSSP191 MOV R6,@>8356       Restore pointer to file name.

        BLWP @DSRLNK        Read data into buffer in VDP RAM.
        DATA 8

        JNE  MSSP192        Jump if no DSR error.
*                           (A DSR error is expected at EOF. Any other error is ignored. File is closed
*                           automatically when the EOF is reached.)

        MOVB @B00,*R3       Add end of page marker to page download buffer.

        CLR  @DSPLYSC       Reset a couple of flags to the default value so that newly loaded page is
        CLR  @NOSCRLL       displayed correctly.
        CLR  @INTIMEO

        MOV  @CRCARDB,R12   Switch serial interface on NanoPEB/RS-232 Card back on.
        SBO  0

        B    @RNDR          Render page.

MSSP192 LI   R0,PABADR+5    Address of character count in PAB.
        BLWP @VSBR          Read number of characters read into MSB of R1.
        SRL  R1,8           Shift to LSB.

        MOV  R1,R2          Copy characters read from VDP RAM buffer to page download buffer.
        LI   R0,BUFADR
        MOV  R3,R1
        BLWP @VMBR

        A    R2,R3          Update pointer in page download buffer.

        JMP  MSSP191        Read next line from file.

Edited by Stuart, Sun Apr 1, 2018 5:15 AM.


#3 retroclouds OFFLINE  

retroclouds

    Stargunner

  • Topic Starter
  • 1,591 posts
  • Location:Germany

Posted Sun Apr 1, 2018 5:40 AM

Thank you Stuart! Looking at some working code certainly helps. I did not look at Ralphs Molesworth book. But going to check it out now :-)

#4 TheBF OFFLINE  

TheBF

    Dragonstomper

  • 557 posts
  • Location:The Great White North

Posted Sun Apr 1, 2018 7:11 AM

Hi everybody,

 

I'm looking for some good working examples of doing disk file I/O operations in assembly language. 

Already looked at section 3 DSR (Device Service Routine) / Disk & File Management in the development resources thread.

 

Lots of good stuff there, but seeing some working code definitely would help.

What I want to accomplish is to create, read, update text files and load binary files.

 

Basically I'd like to write a small programmer's editor for running on the TI-99/4a.

For keeping things simple and compatible with as many disk devices as possible I would only focus on level 3 I/O calls.

 

Now here's where things get tricky. I want to use my spectralibrary for most of the stuff.

I learned that scratchpad memory and VDP memory must be setup in a certain way for DSR calls to work successfully.

 

Currently I'm exclusively using scratchpad memory for all my stuff, so will have to reallocate that to somewhere else. 

That should be possible easily enough. Now here are my questions:

 

1. Does a scratchpad memory map exist, with the minimal requirements for calling DSR routines ?

​My post should help with that.

 

2. How do I need to setup VDP memory so that file I/O is possible. I understand that a PAB must always reside in VDP memory.

    But the disk controller DSR also uses VDP memory for work buffers during file I/O. Does the disk controller DSR always loads files from disk to VDP or is it possible to directly load to RAM ?

>20 bytes for a PAB in free VDP space

>100 bytes for a buffer in free VDP space

 

3. Is it possible to do file I/O calls in pure assembly language or do I have to involve GPL in some way? 
The documentation being focussed on Monitor, Basic, etc. seems to imply that GPL is always involved. Is that a necessity? 

Assembler is all you need.

 

4. What is the easiest way to "interface" with DSR memory setup. I mean would have to switch between my "application workspace" outside of scratch-pad memory, into the "DSR workspace" in scratch-pad memory and return safely.

You must switch to the GPL workspace and then BL to an address in the expansion card ROM that is in R9 of the GPL workspace.

So you MUST make sure R9 has the correct address in it. You search for that address in the ROM card which has a linked list header as the first data in the ROM.

 

5. Any good examples out there? 

See the code below for the minimum calling requirements in Assembler

 

 

On a sidenote, I remember a discussion with InsaneMultitasker many moons ago where it was mentioned that a file operations document was in the works. My memory is a bit blurry on that, can't recall if this was finished or not. 

 

 

Cheers

Filip

 

Hi Filip,

 

I just went through this exercise only I did it in Forth and one little assembler routine.

 

I talk about what I learned here:

http://atariage.com/...rial/?p=3990029

 

The thing that "I" could not find was the minimum amount of Assembly code needed to call a DSR.

 

Here it is in Forth Assembler.  Just put the OP code on the other side to understand it. :-)

The name after CODE: is  just a label in assembler

And the DSRWKSP is any 32 byte memory space you have available.

\ This is the code used to CALL the DSR ROM code
CODE: CALLDSR ( -- )         \ *called with Forth word BLWP
             83E0 LWPI,      \ change to GPL workspace
            *R9 BL,          \ GPL R9 already has the entry address
             0BF0 DATA,      \ This normally has DATA 8 in it. :-)
             DSRWKSP LWPI,   \ back to the dummy workspace
             RTWP,           \ Return to Forth workspace
             NEXT,           \ run the next Forth word
END-CODE

So the outcome of my exercise was to try and document what goes on rather than specific code so that the information is transferable to another computer language.

This may help you from a conceptual point of view.

 

NOTE: Something that I did was simplify the problem by only searching for DSRs in the Floppy disk card.  

The system normally assumes that you search all the cards starting at CRU address >1000 until you find the device (ie DSK1, DSK2, RS232) that you asked for.

Forth thinking is to keep functions doing 1 thing and if you need another thing make another function. So I search at CRU address >1100 ONLY. The floppy disk card.

It goes faster and if I want to search another card in future I will use the same routine but with a different card address.

You may not like this approach. :-)

 

Another thing I did was glue the PAB and the VDP buffer together as 1 piece in VDP memory

Then in the PAB I make the buffer address PAB+>20.  This limits the file buffer to >100 bytes or 1 maximum size record.

It won't work for the Load and Save operations which want a big VDP buffer.

 

Brian



#5 Asmusr ONLINE  

Asmusr

    River Patroller

  • 2,671 posts
  • Location:Denmark

Posted Sun Apr 1, 2018 11:03 AM

My games are usually using all of scratch pad and all VDP RAM, so when I have been using disk files I have saved all scratch pad and the top of VDP RAM to a buffer when the game started up, and restored both again before calling DSRLNK. This is wasting a lot of space since (I assume) only a few bytes really need to be saved and restored, and if you don't require 32K RAM you have to keep the buffer elsewhere in VDP RAM. Using compression might be an idea since most of the VDP RAM buffers are empty anyway and will compress well. Or you could try to map out exactly which bytes need to be saved and restored, but it might differ slightly between disk controllers, nanoPEB, etc.  


Edited by Asmusr, Sun Apr 1, 2018 11:03 AM.


#6 adamantyr OFFLINE  

adamantyr

    Stargunner

  • 1,297 posts

Posted Mon Apr 2, 2018 1:10 AM

Molesworth is definitely the best starting point. I'd also read up on Bruce Harrison's art of assembly articles.

For the most part, the only time you have to be worried about limiting open files is if you intend to use bitmap mode. The problem it has is the color table will overwrite needed FDC data near the top of RAM. You can copy and restore it to its original place.

You always have to load disk data into VDP. The problem the engineers had was, memory was still too expensive to put on the card itself, plus VDP was the only reliable RAM in the system to depend on always being there.

Apparently my Corcomp FDC with the Miller ROMs can do direct to CPU memory sector saves and loads. But since it's ONLY on that card and not emulated I can't utilize it.

My own work, I'm slowly migrating everything to sector access by file. Records work fine but only if you are loading a single unit of data; repeated opens and closes are too slow.

#7 retroclouds OFFLINE  

retroclouds

    Stargunner

  • Topic Starter
  • 1,591 posts
  • Location:Germany

Posted Mon Apr 2, 2018 12:51 PM

My games are usually using all of scratch pad and all VDP RAM, so when I have been using disk files I have saved all scratch pad and the top of VDP RAM to a buffer when the game started up, and restored both again before calling DSRLNK. This is wasting a lot of space since (I assume) only a few bytes really need to be saved and restored, and if you don't require 32K RAM you have to keep the buffer elsewhere in VDP RAM. Using compression might be an idea since most of the VDP RAM buffers are empty anyway and will compress well. Or you could try to map out exactly which bytes need to be saved and restored, but it might differ slightly between disk controllers, nanoPEB, etc.  

 

That brings up some interesting questions.  I started looking at scratchpad usage for the different DSR routines. I would like to come up with a scratch-pad memory map of minimal requirements to be able to call a disk controller DSR. Mind you, I don't think I'm going to use the console (GPL) DSRLNK. It uses too many GPL related memory locations and that conflicts with my own scratchpad usage in my spectralibrary/kernel.

 

The goal I have in mind is to read/write files on disk devices (which would included CF7+ and Nanopeb). I'm planning to look into Paolo's DSR code and make my library compatible with that.

This again does bring up some more questions:

 

1. What scratch-pad memory locations is a DSR allowed to change, to be compatible according to the specification? 

2. Do the disk-controller DSR's use the GPL workspace in scratchpad memory at all ?

3. Did anyone ever do a disassembly of the nanopeb / cf7+ DSR, is it available somewhere ? 

 

ok, a lot more reading to do. Interesting stuff I must say.

 

Also thank you @TheBF, your work on describing what you did in CamelForth is very interesting so taking valuable input there.



#8 InsaneMultitasker OFFLINE  

InsaneMultitasker

    Stargunner

  • 1,963 posts

Posted Mon Apr 2, 2018 12:59 PM

 

3. Did anyone ever do a disassembly of the nanopeb / cf7+ DSR, is it available somewhere ? 

 

I disassembled parts of the nano/cf7 DSR to understand some of the nuances.  Jaime later shared some of the DSR source with me when I was trying to resolve the RS232 problems that surfaced when directly accessing the RS232.   Much of the code mirrors that used by the TI FDC and TI RS232, so if you remain compatible with those two cards, you should be OK.  The one difference is VDP usage, as documented on AA and a few other places, since the device uses 8 bytes to track the mounted volumes.   I have not / will not release my disassembly or related code since the device owner is still actively making and supporting the device.



#9 jedimatt42 ONLINE  

jedimatt42

    Stargunner

  • 1,530 posts
  • Location:Beaverton, OR

Posted Mon Apr 2, 2018 3:03 PM

If you want to experiment with minimal memory to leave for a DSR to call, this info might help.

 

I took these notes when trying to determine what memory I was allowed to use as a DSR implementor:

 

; DSR Notes:
; --
; Available WP Registers: R0-R10  (WP >83E0  GPL WS)
;  R11 == Return address
;  R12 == CRUBASE of current device
; Available Scratchpad: >834A - >836D (inclusive) (except?? >8354 and >8356 are preset to locate PAB in VDP)
 
There was some notes in the hardware design guide that I wasn't allowed to use the last 3 registers, I assume due to BLWP mechanics. 
 
Then, most controllers expect to find a buffer space near the top of VDP... there was another discussion on that, but implementing the equivalent of CALL FILES(1)  to adjust VDP to have just 1 file open at a time, would satisfy a TIFDC. 
 
Then, whatever memory your DSRLNK routine needs. And a PAB + filename... 
 
Console DSRLNK copies the FAC aside, and then restores it... I'm not sure where it copies it to.  That space would have to be left reserved, or you implement a DSRLNK that doesn't copy this cause you aren't using the FAC maybe ?  
 
// As an aside:
My DSR only writes to >8364 - >836A and >83E0 - >83F9, and the PAB that is handed to it for level 3 IO. 
Level 2 IO passes info around in >834C - >8351 and a VDP specified by the caller. 
 
-M@


#10 apersson850 OFFLINE  

apersson850

    Moonsweeper

  • 486 posts

Posted Tue Apr 3, 2018 6:50 AM

When I tried to figure out why a Horizon RAMdisk doesn't work with the UCSD p-system, I realized that there are devices (like later editions of the ROS - RAMdisk Operating System - for that disk) that makes assumptions about how the console's DSRLNK works. The ROS is looking into memory locations in scratch-pad RAM, where the console DSRLNK stores values during its search for the correct routines to run in the DSR. ROS then checks these locations to figure out what to execute, instead of doing it the way TI intended. The result is that ROS broke the ability to use the Horizon RAMdisk with the p-system, since the p-system uses different memory locations to store this data. The p-system's DSRLNK is actually split in two, an initial one, which runs only once during power-up, and the executive part, which runs on each DSR access. But the executive part then benefits from data already figured out by the initial code, to make DSR access faster.

 

This is actually not too bad an idea. In the p-system, a certain number of devices are pre-configured. Like disks, serial channels etc. Then at power up, the system scans for where these physical devices are located. Thus it will figure out the CRU and code entry addresses for each device in advance, and store them. At the actual call, the stored CRU address is used to enable the card, and then the stored code start address is jumped to on the card.

This combines the flexibility of having the disk controller at any address with a quick call sequence on each invocation. The data structures for the p-system actually allow all six (normal max number of disks) floppies (or RAM disks) to have different controllers, even if the TI system does fill the table with the same values for the first three, as TI only had a three drive controller at that time.



#11 Tursi OFFLINE  

Tursi

    Quadrunner

  • 5,031 posts
  • HarmlessLion
  • Location:BUR

Posted Tue Apr 3, 2018 1:55 PM

The Classic99 disk system source code has a lot of information about used memory locations, which are important to some software and which are important to the TI DSR. It's also got code to scribble over some of the common assumption addresses (I was hoping to discourage their use moving forward to improve device compatibility on newer software). The docs are based on years of real-life operation... reading the comments in disk.cpp may provide some hints. :)

#12 Lee Stewart ONLINE  

Lee Stewart

    River Patroller

  • 3,645 posts
  • Location:Silver Run, Maryland

Posted Tue Apr 3, 2018 9:44 PM

...

There was some notes in the hardware design guide that I wasn't allowed to use the last 3 registers, I assume due to BLWP mechanics. 
...
-M@

 

The reason not to write over GPL workspace R13 – R15 (or, at the very least, to restore them) is not, I am pretty sure, for BLWP mechanics, but rather for GPL function:  GPL depends on the current GROM base address being in R13 (usually >9800), timer tick and flags in R14 and the VDP Write Address in R15 (>8C02).

 

More insight is to be had in Tursi’s post just above.

 

...lee



#13 Tursi OFFLINE  

Tursi

    Quadrunner

  • 5,031 posts
  • HarmlessLion
  • Location:BUR

Posted Tue Apr 3, 2018 11:03 PM

The reason not to write over GPL workspace R13 – R15 (or, at the very least, to restore them) is not, I am pretty sure, for BLWP mechanics, but rather for GPL function:  GPL depends on the current GROM base address being in R13 (usually >9800), timer tick and flags in R14 and the VDP Write Address in R15 (>8C02).

 

This is actually pretty important - certain patterns in R14 even prevent resets from working. (The GPL interpreter gets into an interrupt spinloop before it gets a chance to clear the data ;) ).



#14 speccery OFFLINE  

speccery

    Moonsweeper

  • 328 posts

Posted Wed Apr 11, 2018 2:02 PM

Just discovered this thread... interesting background info for the ET-PEB disk DSR development. I have used the classic99 source as the definitive guide, but Ive only read certain parts of the code. In my case Ive tried to minimize the amount of TMS9900 code, and build most of the code in C running on the ARM MCU.

In my case there are no buffers in VDP memory, and the files call is irrelevant as the disk buffers are stored in the memory of the MCU. The DSR still needs to deal with VDP memory, as all disk transaction data is passed in VDP memory. In my design thats pure overhead, but thats the way TI wanted it to be... and enables disk use without memory expansion - although as the ET-PEB provides memory expansion it is moot point in my particular case.

Still, very interesting to learn about the experiences and behavior of other disk systems.

#15 InsaneMultitasker OFFLINE  

InsaneMultitasker

    Stargunner

  • 1,963 posts

Posted Wed Apr 11, 2018 2:35 PM

 

 

1. Does a scratchpad memory map exist, with the minimal requirements for calling DSR routines ?

 

2. How do I need to setup VDP memory so that file I/O is possible. I understand that a PAB must always reside in VDP memory.

 

On a sidenote, I remember a discussion with InsaneMultitasker many moons ago where it was mentioned that a file operations document was in the works. My memory is a bit blurry on that, can't recall if this was finished or not. 

 

 

Cheers

Filip

I don't recall the conversation or document, which doesn't surprise me. It isn't on my radar either. Sorry.

 

To your point about "the PAB must always reside in VDP memory". This is not a rule or a requirement.  There are documented extensions for the PAB and/or buffers residing in CPU memory that are used by Myarc DSRs the IDE card DSR, ASCSCI DSR, maybe the HDX DSR, and perhaps a few others.  The application is somewhat fragmented and inconsistent, which is unfortunate. 

 

If you use CPU RAM directly, you will eliminate compatibility with older VDP-only cards. This leads to cases where a programmer will test the card or operation and if supported, use the faster, more direct CPU RAM transfers.   In other cases, use of a specific peripheral implies availability of CPU transfers.   I always opt for CPU RAM transfers whenever possible but to each his own ;)







Also tagged with one or more of these keywords: pab, dsr, tms9900, file i/o

0 user(s) are browsing this forum

0 members, 0 guests, 0 anonymous users