Jump to content


  • Posts

  • Joined

  • Last visited

  • Days Won


Davideesk last won the day on November 18 2020

Davideesk had the most liked content!

Recent Profile Visitors

209 profile views

Davideesk's Achievements


Newbie (1/14)

  • One Year In
  • Conversation Starter Rare
  • Reacting Well Rare
  • First Post Rare
  • Dedicated Rare

Recent Badges



  1. Ah, so I made a mistake by using the execute_dos_command function with the POSITION command. The function expects an input string to end with a null terminator (0x00). Since the arguments for the POSITION command are binary, it is likely that one of the five arguments will be 0x00. I've created a new function, execute_dos_command_n, to take this into account. It takes in an additional argument for the length of the string. For the POSITION command, this argument would be 6. I've updated the attached filepos-example code to use this function instead of the original. ; execute_dos_command_n ; Processes a CMDR-DOS channel 15 command. Use when you know the exact number of characters ; that need to be sent. This is needed for the POSITION command to be processed correctly. ; Arguments: ; r0 = Pointer to a string that is a valid DOS command. ; r1L = Length of the input string ; Command Examples: ; "MD:FOO" creates a new directory called FOO ; "CD:FOO" will enter the FOO directory ; "CD:.." will enter the parent directory ; "RD:FOO" will delete the FOO directory ; Assumptions: ; * SD Card is being used and is set to device #8 execute_dos_command_n: !zone { lda r1L bne .setup ; Continue if the length > 0 rts ; End early if the length was 0. .setup: ldx #15 ; Open Channel 15 to send DOS commands. jsr CHKIN lda #8 ; SD Card in device #8 jsr LISTEN lda #$6F ; $6X = LISTEN to channel X, so $6F = LISTEN to channel 15 jsr SECOND .loop_init: ldy #0 ; Start at the first character. .send_char: lda (r0),Y ; Read the current character jsr IECOUT ; Send character to the serial bus iny ; Move to the next character cpy r1L bne .send_char ; Keep looping if y < r1L .cleanup_and_exit: jsr UNLSN ; Stop listening and send the command. jmp CLRCHN ; Close the channel and return }
  2. Hello again everyone. I've been wanting to make a game for the X16 lately, but for what I want to do I needed to figure out a good way to stream data off a file from any random position. You can't do that yet within the r38 emulator (as far as I know). You would have to split up the file into smaller ones. However, you can do this with the POSITION command in CMDR-DOS. This does mean you must have an SD card for this to work. The syntax for the POSITION command is: You have to specify both the channel the file is on and the 32-bit position you want to use. Those 5 arguments must be actual byte values, and not ascii representations of the values! In BASIC, it is as simple as: Assembly is more verbose, but it is basically the same idea. You first open the file, set the file offset, read bytes into memory, then close the file. Because there is a lot more code to go through I have included an attachment with the source code of the example. I personally use the ACME assembler, but it shouldn't be difficult to adapt to other assemblers. You should be able to get a program file when you assemble main.asm. Once you have the .prg file built, place it and the TEST file in an SD card image and run it in the emulator. The TEST file includes a bunch of ascii text. I'm interested in retrieving the "HELLO X16!" string without going through any of the padding. The first thing we need to do is open the file. (All of the following code will be in the attachment, so you don't need to copy & paste) ; fs_open ; Opens a file to be read. ; Arguments: ; r0 = Pointer to a null-terminated string that is the filepath to open. ; r1L = Channel to open. ; Assumptions: ; * SD Card is being used and is set to device #8 ; * String in r0 is less than 256 characters. ; * Channel is not 0, 1, or 15. fs_open: !zone { +set_ram_bank RAM_BANK_KERNAL_DOS ; Make sure the ram bank is set to 0. lda (r0) ; Load the first character in the string bne .setup ; Checking for the null-terminator rts ; End early if the input is an empty string. .setup: ldx r1L ; Open passed in channel jsr CHKIN lda #8 ; SD Card in device #8 jsr LISTEN lda r1L ora #$F0 ; $FX = Open file in channel X (r1L) jsr SECOND .loop_init: ldy #0 ; Start at the first character. .send_char: lda (r0),Y ; Read the current character beq .cleanup_and_exit ; Exit if the byte is the null terminator jsr IECOUT ; Send character to the serial bus iny ; Move to the next character bra .send_char ; Keep looping .cleanup_and_exit: jmp UNLSN ; Stop listening and send the filename. } This function was adapted from my execute_dos_command function that I shared in my original post, since opening up channel 15 is how you execute dos commands in the first place. Do note that the high-ram bank must be set to zero for this code to work. Speaking of which, we are going to use a modified execute_dos_command function to run the POSITION dos command, but first we have to setup that command in RAM: ; The channel 15 command that sets the position of a file. DOS_FILE_POSITION: !scr "P" DOS_FILE_POSITION_CHANNEL: !byte 0 DOS_FILE_POSITION_OFFSET: !byte 0, 0, 0, 0 DOS_FILE_POSITION is used as the string pointer for the command. DOS_FILE_POSITION_CHANNEL points to the channel id, and DOS_FILE_POSITION_OFFSET points to the 32-bit offset. In the attachment I created a bunch of macros to help me simplify the code. I'm not going to go through them since it would take too long, so here is an equivalent to the line 20 of the basic code: lda #2 ; Using channel 2 sta DOS_FILE_POSITION_CHANNEL ; Set the channel of the file to 2 lda #$80 ; "HELLO X16!" is located at offset $80 of the TEST file sta DOS_FILE_POSITION_OFFSET ; Store $80 to the low byte ; I technically don't need these 3, since they are never modified. ; stz DOS_FILE_POSITION_OFFSET + 1 ; Store 0 to the middle-low byte ; stz DOS_FILE_POSITION_OFFSET + 2 ; Store 0 to the middle-high byte ; stz DOS_FILE_POSITION_OFFSET + 3 ; Store 0 to the high byte lda #<DOS_FILE_POSITION ; Load the DOS_FILE_POSITION pointer into r0 sta r0L lda #>DOS_FILE_POSITION sta r0H lda #6 sta r1L ; Length of the POSITION command (6 bytes) jsr execute_dos_command_n ; Execute the command to set the file offset to $80 Now that we have the position ready, we now need to read the bytes from the file. To do this we need to set the ROM bank to 2, since that bank contains the function to read bytes. That function is called file_read_block with the arguments: .A is the number of bytes to read and .Y/.X is the destination address to write to. To help with this I wrote some macros to save the ROM bank & call the file_read_block function: ; Read bytes from a file into RAM. !macro m_fs_read_to_address .dest, .length { ldx #<.dest ldy #>.dest lda #.length jsr file_read_block ; ROM bank must be set to 2 before this is called! } ; Saves the current ROM bank and sets it to the CBDOS bank. !macro m_fs_start_reading { +push_current_rom_bank_onto_stack +set_rom_bank ROM_BANK_CBDOS ; ROM_BANK_CBDOS = 2 } ; Restores the previous ROM bank. !macro m_fs_stop_reading { +pull_current_rom_bank_from_stack } You can find the macros: set_rom_bank, push_current_rom_bank_onto_stack, and pull_current_rom_bank_from_stack in the banks.inc file in the attachments source code. After the bytes are read, the file finally needs to be closed so that the channel can be used for other files. To do that you simple call the fs_close function. ; fs_close ; Closes a channel so that it can be used again. ; Arguments: ; r0L = Channel to close. ; Assumptions: ; * SD Card is being used and is set to device #8 ; * String in r0 is less than 256 characters. ; * Channel is not 0, 1, or 15. fs_close: !zone { ldx r0L ; Open passed in channel jsr CHKIN lda #8 ; SD Card in device #8 jsr LISTEN lda r0L ora #$E0 ; $EX = Close file in channel X (r0L) jsr SECOND jmp CLRCHN ; Close the channel and return } When you look at the main.asm file, you'll see these macros being used: FILENAME: !SCR "TEST", 0 CHANNEL_FILE = 2 ; Channel to use. Can be any value within [2, 14] SRC_OFFSET = $80 ; Position within the file DEST_ADDRESS = $20 ; RAM address to write to READ_LENGTH = 10 ; Number of bytes to copy main: +m_fs_open CHANNEL_FILE, FILENAME ; Open the file +m_fs_set_file_position CHANNEL_FILE, SRC_OFFSET ; Set the current position of the file. +m_fs_start_reading ; Saves the current ROM bank, and sets it to the CBDOS code bank. +m_fs_read_to_address DEST_ADDRESS, READ_LENGTH ; Read data from the file into RAM. +m_fs_stop_reading ; Restores the original ROM bank. +m_fs_close CHANNEL_FILE ; The channel must be closed before using it again. exit: rts ; Return to basic. I'm hoping that those macros make it easy to understand what is happening conceptually. You can look at fs.asm for more information on what they are actually doing. The indenting isn't required, I just think it looks nice (like python). When you run the code in the emulator you'll see that nothing happens on the basic screen. When you dump the RAM, you should see that the "HELLO X16!" message appeared at offset $20. If you have any question or comments, then please leave a reply and I'll try to get back to you as soon I can. filepos-example-v1.1.zip
  3. You can find BASIN in the programmer's reference manual under the Commodore 64 API compatibility section. Here is the include file I am currently using: https://pastebin.com/raw/vuBfkYpZ
  4. I couldn't find any resources on how to create and change directories on the SD card using assembly, so I wrote some helper functions based off the code in "x16-rom/basic/x16additions.s". These functions show how to use the KERNAL API to send commands and get file/directory names from CMDR-DOS. I wrote the functions to be simple, so error & SD card checking will be left up to the reader. The code itself was written using the ACME assembler, but it should be easy to convert it to other assemblers. Code: https://gist.github.com/DavidSM64/a036a622f4b7a2d8364c355e72766eb7 The 3 functions are: execute_dos_command Takes in a string pointer that contains a channel 15 command and executes it. A table of commands can be found here: https://github.com/commanderx16/x16-rom/tree/master/dos (It is the large table that starts with "BLOCK-ALLOCATE") Examples: "MD:FOO" = Creates a new directory called FOO "CD:FOO" = Enters the FOO directory "CD:.." = Enters the parent directory "RD:FOO" = Deletes the FOO directory get_dos_directory Takes in a string pointer that contains valid directory syntax (see examples), the length of that input string, and an output pointer where the result will be written to. This function isn't particularly useful, since it also outputs the formatting used in BASIC. Examples: "$" = List out everything in the current directory "$:*=P" = Only list out files "$:*=D" = Only list out directories "$:FOO*=D" = Only list out directories that start with FOO (FOO, FOO2, FOOBAR, etc.) The output will look like this: The first 4 bytes can be ignored For each line: 2 bytes at the start for the block count Then follows a certain number of bytes (might be a variable amount, the line ends with $00) get_dos_dir_entries Same inputs as get_dos_directory This function only outputs the file / directory names, separated by a $00. Returns the number of entries in the current directory into the accumulator. The output will look like this: This will not show the file type, but files typically have an extension while directories do not.
  • Create New...

Important Information

Please review our Terms of Use