Loading files from disk using firmware (C & ASM with SDCC) Pincha aquí para verlo en español In this tutorial we will see how to read files from disk in the easiest way possible, using the Firmware. When a program is executed with run"program, ther disk rom is disabled, so you have to make a trick to re-enable it. I have borrowed an example from Kevin Thacker as a reference to do so. Basically we use the functions of the firmware mc_start_program y kl_rom_walk to use disc functions. Then for loading a file, nothing easier than using the functions cas_in_open, cas_in_direct y cas_in_close. For examples of loading files, I used ConvImgCPC to convert some photos of some friends at a Christian camp to scr format. Let's see the first program, which loads an image on the screen: //////////////////////////////////////////////////////////////////////// // Load01.c // Mochilote - www.cpcmania.com //////////////////////////////////////////////////////////////////////// #include <stdio.h> #include <string.h> //////////////////////////////////////////////////////////////////////// //SetupDOS - Prepare DOS for file load //////////////////////////////////////////////////////////////////////// void SetupDOS() { //Based on http://cpctech.cpc-live.com/source/loader.html __asm ld l, 2 (ix) ;stack return address ld h, 3 (ix) ;stack return address ld (_stack+1), hl ;;------------------------------------------------------------------------ ;; store the drive number the loader was run from ld hl,(#0xbe7d) ld a,(hl) ld (_drive+1),a ;;------------------------------------------------------------------------ ld c,#0xff ;; disable all roms ld hl, #_start222 ;; execution address for program call #0xbd16 ;;mc_start_program ;; start it _start222:: nop call #0xbccb ;;kl_rom_walk ;; enable all roms ;;------------------------------------------------------------------------ ;; when AMSDOS is enabled, the drive reverts back to drive 0! ;; This will restore the drive number to the drive the loader was run from _drive: ld a, #0x00 ld hl,(#0xbe7d) ld (hl),a _stack: ld hl, #0x0000 push hl //one for sdcc ix pop push hl //two for return address __endasm; } //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// //Load filename to address //////////////////////////////////////////////////////////////////////// unsigned char nFileNameLen = 0; void LoadFile(char *sFileName, char *pLoadAddress) { nFileNameLen = strlen(sFileName); __asm ;; B = length of the filename in characters ld hl, #_nFileNameLen; ld b, (hl) ;; HL = address of the start of the filename LD L, 4 (IX) ;sFileName LD H, 5 (IX) ;sFileName ;; DE = address of a 2k buffer ;; in disc mode: this buffer is not used when CAS IN DIRECT ;; firmware function is used, so it is safe to put it anywhere ;; you want. ld de, #0x0 ;; firmware function to open a file for reading call #0xbc77 ;;cas_in_open ;; firmware function to load the entire file ;; this will work with files that have a AMSDOS header (ASCII ;; files do not have a header) ;; HL = load address LD L, 6 (IX) ;pLoadAddress LD H, 7 (IX) ;pLoadAddress ;; read file call #0xbc83 ;;cas_in_direct ;; firmware function to close a file opened for reading call #0xbc7a ;;cas_in_close __endasm; } //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// //SetColor //////////////////////////////////////////////////////////////////////// void SetColor(unsigned char nColorIndex, unsigned char nPaletteIndex) { __asm ld a, 4 (ix) ld b, 5 (ix) ld c, b call #0xBC32 ;SCR SET INK __endasm; } //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// //SetPalette //////////////////////////////////////////////////////////////////////// void SetPalette(const unsigned char *pPalette, unsigned char nNumColors) { unsigned char nColor = 0; for(nColor = 0; nColor < nNumColors; nColor++) SetColor(nColor, pPalette[nColor]); } //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// //SetMode //////////////////////////////////////////////////////////////////////// void SetMode(unsigned char nMode) { __asm ld a, 4 (ix) call #0xBC0E ;SCR_SET_MODE __endasm; } //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// //main //////////////////////////////////////////////////////////////////////// main() { SetupDOS(); SetMode(0); LoadFile("01.scr", (char *)0xC000); SetPalette((char *)0xD7D1, 16); while(1); return 0; } //////////////////////////////////////////////////////////////////////// If you compile and load the binary we get the following: As you can see we have SetupDOS function that prepares the system to use the disk rom and we have the function LoadFile that loads the file that you say in the direction we want. So easy and simple :-) If we look at the role SetupDOS see that in the beginning the return address of the function is saved, this is so because when call mc_start_program, the stack is reset, is important to call SetupDOS at the beginning of main function and not use local variables in main, because would be added to the stack and when returning from SetupDOS, would have garbage. If you need variables in the main function we can use global or simply call a new function. The following example demonstrates this. The program is a slideshow of pictures: //////////////////////////////////////////////////////////////////////// // Load02.c // Mochilote - www.cpcmania.com //////////////////////////////////////////////////////////////////////// #include <stdio.h> #include <string.h> //////////////////////////////////////////////////////////////////////// //SetupDOS - Prepare DOS for file load //////////////////////////////////////////////////////////////////////// void SetupDOS() { //Based on http://cpctech.cpc-live.com/source/loader.html __asm ld l, 2 (ix) ;stack return address ld h, 3 (ix) ;stack return address ld (_stack+1), hl ;;------------------------------------------------------------------------ ;; store the drive number the loader was run from ld hl,(#0xbe7d) ld a,(hl) ld (_drive+1),a ;;------------------------------------------------------------------------ ld c,#0xff ;; disable all roms ld hl, #_start222 ;; execution address for program call #0xbd16 ;;mc_start_program ;; start it _start222:: nop call #0xbccb ;;kl_rom_walk ;; enable all roms ;;------------------------------------------------------------------------ ;; when AMSDOS is enabled, the drive reverts back to drive 0! ;; This will restore the drive number to the drive the loader was run from _drive: ld a, #0x00 ld hl,(#0xbe7d) ld (hl),a _stack: ld hl, #0x0000 push hl //one for sdcc ix pop push hl //two for return address __endasm; } //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// //Load filename to address //////////////////////////////////////////////////////////////////////// unsigned char nFileNameLen = 0; void LoadFile(char *sFileName, char *pLoadAddress) { nFileNameLen = strlen(sFileName); __asm ;; B = length of the filename in characters ld hl, #_nFileNameLen; ld b, (hl) ;; HL = address of the start of the filename LD L, 4 (IX) ;sFileName LD H, 5 (IX) ;sFileName ;; DE = address of a 2k buffer ;; in disc mode: this buffer is not used when CAS IN DIRECT ;; firmware function is used, so it is safe to put it anywhere ;; you want. ld de, #0x0 ;; firmware function to open a file for reading call #0xbc77 ;;cas_in_open ;; firmware function to load the entire file ;; this will work with files that have a AMSDOS header (ASCII ;; files do not have a header) ;; HL = load address LD L, 6 (IX) ;sFileName LD H, 7 (IX) ;sFileName ;; read file call #0xbc83 ;;cas_in_direct ;; firmware function to close a file opened for reading call #0xbc7a ;;cas_in_close __endasm; } //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// //SetColor //////////////////////////////////////////////////////////////////////// void SetColor(unsigned char nColorIndex, unsigned char nPaletteIndex) { __asm ld a, 4 (ix) ld b, 5 (ix) ld c, b call #0xBC32 ;SCR SET INK __endasm; } //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// //SetPalette //////////////////////////////////////////////////////////////////////// void SetPalette(const unsigned char *pPalette, unsigned char nNumColors) { unsigned char nColor = 0; for(nColor = 0; nColor < nNumColors; nColor++) SetColor(nColor, pPalette[nColor]); } //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// //SetMode //////////////////////////////////////////////////////////////////////// void SetMode(unsigned char nMode) { __asm ld a, 4 (ix) call #0xBC0E ;SCR_SET_MODE __endasm; } //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// //LoadLoop //////////////////////////////////////////////////////////////////////// void LoadLoop() { unsigned char BlackPalette[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; int nImage = 0; char sFile[32]; while(1) { for(nImage = 1; nImage < 10; nImage++) { sprintf(sFile, "0%d.scr", nImage); LoadFile(sFile, (char *)0x6000); SetPalette((char *)BlackPalette, 16); memcpy((char *)0xC000, (char *)0x6000, 0x3FFF); SetPalette((char *)0xD7D1, 16); } } } //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// //main //////////////////////////////////////////////////////////////////////// main() { SetupDOS(); SetMode(0); LoadLoop(); return 0; } //////////////////////////////////////////////////////////////////////// If you compile and load the binary we get the following:
In this simple way we can load files and for example program a loader in c :-). You could download a zip with all files (source code, bat to compile, binary and dsk's) here: Files.zip |
www.CPCMania.com 2014 |