alexfru / SmallerC

Simple C compiler
BSD 2-Clause "Simplified" License
1.35k stars 155 forks source link

Code and data in ROM and RAM on 80188? #18

Closed cocus closed 6 years ago

cocus commented 6 years ago

Hello, I was looking for a C compiler for an 80C188 processor and this one came to be a really nice one. However, and after looking in the Wiki, I found something that might cripple my intentions. This board happens to have a 128k RAM and 512k flash. If the flat image produced by the compiler ends up in the read only map of the processor (in the flash), some code (ie global rw variables) will become read only. I was looking the smlrl.c file but I couldn't tell how to force that section to be in other place (for my purposes, at the bottom of the memory map, 0x0). I know that I'll need an initializer stub that should run prior to my code.

So, my question is, it possible to achieve this without a lot of fiddling in the source file of smlrl? Or maybe some kind of hack using other tool alongside SmallerC?

Sorry if this is not a proper "issue", but I wanted to ask directly to the author.

alexfru commented 6 years ago

Smaller C & 8086/8088/80186/80188/80286 & non-DOS systems

General Issues:

Therefore, compiling code for 8086/8088/80186/80188/80286 requires:

Non-DOS systems may require additional/special support for executable files, most notably:

xxxx

If you're OK with the implications (.EXEs instead of flats, one data+stack segment, no 32-bit types), I can provide further help.

cocus commented 6 years ago

Alexey, that should be more than enough, since its just for fun, and I can workaround some of the culprits.

If I get it correctly, the linker, when using the small model puts the DS and CS in different addresses, but they can't be specified via the arguments (at least, I didn't saw how to do that in the wiki).

I think that if the code only uses relative jumps all the time and I manually put the .text section wherever I want, shouldn't be a problem, however, offsets of absoulte calls won't be correct, since they would be the ones assigned by the linker. If the linker could put these sections on a specified address, then I think it will work.

Putting that aside, I should create a program that strips out the .exe header. Also, after the very end of the header, there should be the entry point; or I might be able to workaround this by adding a hardcoded relative jump to the entrypoint (the one defined in the .exe header).

Sorry if I don't have the enough knowledge about this, 16 bit x86 is not a target that I have used prior.

alexfru commented 6 years ago

Alright, here's a sample loader.

// Loader for small memory model .EXEs produced with Smaller C.
// File: ldr.c
// Compile: smlrcc -dosh ldr.c -o ldr.exe
// Test: ldr.exe ldrtst.exe
#include <stdint.h>
#include <inttypes.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

void error(char* format, ...)
{
  va_list vl;
  va_start(vl, format);
  vprintf(format, vl);
  va_end(vl);
  exit(EXIT_FAILURE);
}

FILE* Fopen(const char* filename, const char* mode)
{
  FILE* stream = fopen(filename, mode);
  if (!stream)
    error("Can't open/create file '%s'\n", filename);
  return stream;
}

void Fseek(FILE* stream, long offset, int whence)
{
  int r = fseek(stream, offset, whence);
  if (r)
    error("Can't seek a file\n");
}

void Fread(void* ptr, size_t size, FILE* stream)
{
  size_t r = fread(ptr, 1, size, stream);
  if (r != size)
    error("Can't read a file\n");
}

void* Malloc(size_t size)
{
  void* p = malloc(size);
  if (!p)
    error("Out of memory\n");
  return p;
}

#define C_ASSERT(expr) extern char CAssertExtern[(expr)?1:-1]

typedef struct
{
  uint8_t  Signature[2];
  uint16_t PartPage;
  uint16_t PageCnt;
  uint16_t ReloCnt;
  uint16_t HdrSize;
  uint16_t MinAlloc;
  uint16_t MaxAlloc;
  uint16_t InitSs;
  uint16_t InitSp;
  uint16_t ChkSum;
  uint16_t InitIp;
  uint16_t InitCs;
  uint16_t ReloOff;
  uint16_t OverlayNo;
  uint16_t FirstRelo[2];
} tDosExeHeader;

C_ASSERT(sizeof(tDosExeHeader) == 32);

#if defined(__SMALLER_C__) && defined(__HUGE__)
void run(uint16_t cs, uint16_t ip,
         uint16_t ss, uint16_t sp)
{
  asm(
  "mov ax, [bp+8+4*0]\n" // cs
  "mov bx, [bp+8+4*1]\n" // ip
  "mov cx, [bp+8+4*2]\n" // ss
  "mov dx, [bp+8+4*3]\n" // sp
  "cli\n"
  "mov ss, cx\n" // new ss:sp
  "mov sp, dx\n"
  "sti\n"
  "push ax\n"    // push cs:ip
  "push bx\n"
  "retf"
  );
}
#else
void run(uint16_t cs, uint16_t ip,
         uint16_t ss, uint16_t sp)
{
  (void)cs;
  (void)ip;
  (void)ss;
  (void)sp;
}
#endif

int main(int argc, char* argv[])
{
  FILE* f;
  tDosExeHeader header;
  uint32_t fsize;
  uint32_t hsize;
  uint32_t tsize;
  uint32_t dsize;
  uint32_t bsize;
  uint32_t asize;
  uint8_t* code;
  uint8_t* data;
  uintptr_t code_aligned;
  uintptr_t data_aligned;
  uint16_t cs;
  uint16_t ss;

  if (argc < 2)
    error("Usage: ldr.exe <program.exe>\n");

  f = Fopen(argv[1], "rb");
  Fread(&header, sizeof header, f);

  if (header.Signature[0] != 'M' || header.Signature[1] != 'Z')
    error("Not MZ .EXE header\n");

  fsize = (header.PageCnt - 1) * UINT32_C(512) + header.PartPage;
  printf("File size: %"PRIu32"\n", fsize);

  hsize = header.HdrSize * UINT32_C(16);
  printf("  MZ .EXE header size: %"PRIu32"\n", hsize);
  if (header.HdrSize != 2)
    error("header.HdrSize: %u (expected: 2)\n", header.HdrSize);
  if (header.ReloCnt != 0)
    error("header.ReloCnt: %u (expected: 0)\n", header.ReloCnt);
  if (header.InitCs != 0xFFFE)
    error("header.InitCs: 0x%X (expected: 0xFFFE)\n", header.InitCs);

  tsize = (uint16_t)(header.InitSs - header.InitCs) * UINT32_C(16) - hsize;
  printf("  .text (follows MZ .EXE header) size: %"PRIu32"\n", tsize);
  if (tsize > 65536 - hsize)
    error(".text is too big\n");

  dsize = fsize - hsize - tsize;
  printf("  .rodata + .data (follow .text) size: %"PRIu32"\n", dsize);
  if (dsize > 65536)
    error(".rodata + .data is too big\n");

  printf("Not stored in file:\n");
  bsize = header.InitSp - dsize;
  printf("  .bss + stack (follow .*data) size: %"PRIu32"\n", bsize);
  if (header.InitSp < dsize)
    error("header.InitSp is too small\n");

  printf("Memory DOS allocates after .*data for .bss + stack + etc:\n");
  asize = header.MinAlloc * UINT32_C(16);
  printf("  header.MinAlloc * 16: %"PRIu32"\n", asize);
  if (asize < bsize)
    error("header.MinAlloc is too small\n");

  printf("  header.MaxAlloc * 16: %"PRIu32"\n", header.MaxAlloc * UINT32_C(16));
  if (header.MaxAlloc < header.MinAlloc)
    error("header.MaxAlloc < header.MinAlloc\n");

  code = Malloc(tsize + 15);
  memset(code, 0xCC, tsize + 15); // fill with int3
  code_aligned = ((uintptr_t)code + 15) / 16 * 16;
  Fread((void*)code_aligned, tsize, f);

  data = Malloc(header.InitSp + asize + 15);
  memset(data, 0x80, header.InitSp + asize + 15); // fill with 0x80
  data_aligned = ((uintptr_t)data + 15) / 16 * 16;
  Fread((void*)data_aligned, dsize, f);

  fclose(f);

  cs = code_aligned / 16 + header.InitCs;
  ss = data_aligned / 16;
  run(cs, header.InitIp, ss, header.InitSp);

  return 0;
}
alexfru commented 6 years ago

Here's a test app.

// Small memory model test .EXE.
// File: ldrtst.c
// Compile: smlrcc -doss ldrtst.c -o ldrtst.exe
// Test: ldr.exe ldrtst.exe
#include "80186.h"
#include <stdio.h>

char dstr[] = ".data";
int baint[5];

signed char sc = -1;
unsigned char uc = 255;

int main(void)
{
  int i;
  printf(".rodata string: \"%s\"\n", ".rodata");
  printf(".data string: \"%s\"\n", dstr);
  printf(".bss ints (must be zero):\n");
  for (i = 0; i < sizeof baint/sizeof baint[0]; i++)
    printf("  %d\n", baint[i]);

  printf("Testing movzx and movsx macros:\n");
  printf("  1 * 255: %d\n", 1 * uc);
  printf("  1 * -1: %d\n", 1 * sc);
  return 0;
}
alexfru commented 6 years ago

And here's an include file for the movzx and movsx macros. Something I didn't spend much time on, but it seems to be working so far.

// File: 80186.h
//
// To compile your code for 80186/80188/80286 include this file
// at the top of every C file.
//
// This is only for the tiny and small memory models of Smaller C
// (i.e. for use with options: -tiny/-dost, -small/-doss).
// Other models use 32-bit registers unavailable on any x86 CPU
// earlier than 80386.
//
// This is for use with NASM only (not YASM or FASM).
asm(
"cpu 186                                                               \n"
"                                                                      \n"
"%define ___ReGs___Lo8___ \"alblcldl\"                                 \n"
"%define ___ReGs___Hi8___ \"ahbhchdh\"                                 \n"
"%define ___ReGs___16___ \"axbxcxdxsidibpsp\"                          \n"
"                                                                      \n"
"%macro movzx 2.nolist                                                 \n"
"  %push __CTX_MOVZX__                                                 \n"
"                                                                      \n"
"  %assign %$r_n 8 ; not 8-bit register                                \n"
"  %ifidni %2,al                                                       \n"
"    %assign %$r_n 0                                                   \n"
"  %endif                                                              \n"
"  %ifidni %2,bl                                                       \n"
"    %assign %$r_n 1                                                   \n"
"  %endif                                                              \n"
"  %ifidni %2,cl                                                       \n"
"    %assign %$r_n 2                                                   \n"
"  %endif                                                              \n"
"  %ifidni %2,dl                                                       \n"
"    %assign %$r_n 3                                                   \n"
"  %endif                                                              \n"
"  %ifidni %2,ah                                                       \n"
"    %assign %$r_n 4                                                   \n"
"  %endif                                                              \n"
"  %ifidni %2,bh                                                       \n"
"    %assign %$r_n 5                                                   \n"
"  %endif                                                              \n"
"  %ifidni %2,ch                                                       \n"
"    %assign %$r_n 6                                                   \n"
"  %endif                                                              \n"
"  %ifidni %2,dh                                                       \n"
"    %assign %$r_n 7                                                   \n"
"  %endif                                                              \n"
"                                                                      \n"
"  %assign %$l_n 8 ; not 16-bit register                               \n"
"  %ifidni %1,ax                                                       \n"
"    %assign %$l_n 0                                                   \n"
"  %endif                                                              \n"
"  %ifidni %1,bx                                                       \n"
"    %assign %$l_n 1                                                   \n"
"  %endif                                                              \n"
"  %ifidni %1,cx                                                       \n"
"    %assign %$l_n 2                                                   \n"
"  %endif                                                              \n"
"  %ifidni %1,dx                                                       \n"
"    %assign %$l_n 3                                                   \n"
"  %endif                                                              \n"
"  %ifidni %1,si                                                       \n"
"    %assign %$l_n 4                                                   \n"
"  %endif                                                              \n"
"  %ifidni %1,di                                                       \n"
"    %assign %$l_n 5                                                   \n"
"  %endif                                                              \n"
"  %ifidni %1,bp                                                       \n"
"    %assign %$l_n 6                                                   \n"
"  %endif                                                              \n"
"  ;%ifidni %1,sp                                                      \n"
"  ;  %assign %$l_n 7                                                  \n"
"  ;%endif                                                             \n"
"                                                                      \n"
"  %if %$l_n == 8                                                      \n"
"    %fatal expected word register (other than sp) in left operand     \n"
"  %endif                                                              \n"
"                                                                      \n"
"  %if %$r_n != 8                                                      \n"
"    ; right operand is register                                       \n"
"    %if %$l_n < 4                                                     \n"
"      ; movzx hi_lo, reg                                              \n"
"      %if %$l_n != %$r_n                                              \n"
"        %substr %$reg_lo_str ___ReGs___Lo8___ (%$l_n*2+1), 2          \n"
"                           ; 2-char string with reg name, e.g. \"al\" \n"
"        %deftok %$reg_lo %$reg_lo_str ; tokenized to e.g. al          \n"
"        mov %$reg_lo, %2                                              \n"
"      %endif                                                          \n"
"      %substr %$reg_hi_str ___ReGs___Hi8___ (%$l_n*2+1), 2            \n"
"      %deftok %$reg_hi %$reg_hi_str                                   \n"
"      mov %$reg_hi, 0                                                 \n"
"    %else                                                             \n"
"      ; movzx non-hi_lo, reg                                          \n"
"      mov %1, ax                                                      \n"
"      %if %$r_n != 0                                                  \n"
"        mov al, %2                                                    \n"
"      %endif                                                          \n"
"      mov ah, 0                                                       \n"
"      xchg ax, %1                                                     \n"
"    %endif                                                            \n"
"  %else                                                               \n"
"    ; right operand is memory                                         \n"
"    %if %$l_n < 4                                                     \n"
"      ; movzx hi_lo, mem                                              \n"
"      %substr %$reg_lo_str ___ReGs___Lo8___ (%$l_n*2+1), 2            \n"
"      %deftok %$reg_lo %$reg_lo_str                                   \n"
"      %substr %$reg_hi_str ___ReGs___Hi8___ (%$l_n*2+1), 2            \n"
"      %deftok %$reg_hi %$reg_hi_str                                   \n"
"      mov %$reg_lo, %2                                                \n"
"      mov %$reg_hi, 0                                                 \n"
"    %else                                                             \n"
"      ; movzx non-hi_lo, mem                                          \n"
"      ; Strip leading \"byte\" from %2 for lea.                       \n"
"      %define %$mem %2                                                \n"
"      %defstr %$mem_str %2                                            \n"
"      %substr %$pfx %$mem_str 1,4                                     \n"
"      %ifidni %$pfx, \"byte\"                                         \n"
"        %substr %$pfx %$mem_str 5                                     \n"
"        %ifidn %$pfx, \" \"                                           \n"
"          %substr %$mem_str %$mem_str 5,-1                            \n"
"          %deftok %$mem %$mem_str                                     \n"
"        %endif                                                        \n"
"        %ifidn %$pfx, `\t`                                            \n"
"          %substr %$mem_str %$mem_str 5,-1                            \n"
"          %deftok %$mem %$mem_str                                     \n"
"        %endif                                                        \n"
"        %ifidn %$pfx, \"[\"                                           \n"
"          %substr %$mem_str %$mem_str 5,-1                            \n"
"          %deftok %$mem %$mem_str                                     \n"
"        %endif                                                        \n"
"      %endif                                                          \n"
"      ; TBD: move segment prefix (if any) from \"lea ..., [seg:...]\" \n"
"      ; to \"mov ..., [seg:...]\"                                     \n"
"      lea  %1, %$mem                                                  \n"
"      xchg bx, %1                                                     \n"
"      mov  bl, [bx]                                                   \n"
"      mov  bh, 0                                                      \n"
"      xchg bx, %1                                                     \n"
"    %endif                                                            \n"
"  %endif                                                              \n"
"                                                                      \n"
"  %pop ; __CTX_MOVZX__                                                \n"
"%endm                                                                 \n"
"                                                                      \n"
"%macro movsx 2.nolist                                                 \n"
"  %push __CTX_MOVSX__                                                 \n"
"                                                                      \n"
"  %assign %$r_n 8 ; not 8-bit register                                \n"
"  %ifidni %2,al                                                       \n"
"    %assign %$r_n 0                                                   \n"
"  %endif                                                              \n"
"  %ifidni %2,bl                                                       \n"
"    %assign %$r_n 1                                                   \n"
"  %endif                                                              \n"
"  %ifidni %2,cl                                                       \n"
"    %assign %$r_n 2                                                   \n"
"  %endif                                                              \n"
"  %ifidni %2,dl                                                       \n"
"    %assign %$r_n 3                                                   \n"
"  %endif                                                              \n"
"  %ifidni %2,ah                                                       \n"
"    %assign %$r_n 4                                                   \n"
"  %endif                                                              \n"
"  %ifidni %2,bh                                                       \n"
"    %assign %$r_n 5                                                   \n"
"  %endif                                                              \n"
"  %ifidni %2,ch                                                       \n"
"    %assign %$r_n 6                                                   \n"
"  %endif                                                              \n"
"  %ifidni %2,dh                                                       \n"
"    %assign %$r_n 7                                                   \n"
"  %endif                                                              \n"
"                                                                      \n"
"  %assign %$l_n 8 ; not 16-bit register                               \n"
"  %ifidni %1,ax                                                       \n"
"    %assign %$l_n 0                                                   \n"
"  %endif                                                              \n"
"  %ifidni %1,bx                                                       \n"
"    %assign %$l_n 1                                                   \n"
"  %endif                                                              \n"
"  %ifidni %1,cx                                                       \n"
"    %assign %$l_n 2                                                   \n"
"  %endif                                                              \n"
"  %ifidni %1,dx                                                       \n"
"    %assign %$l_n 3                                                   \n"
"  %endif                                                              \n"
"  %ifidni %1,si                                                       \n"
"    %assign %$l_n 4                                                   \n"
"  %endif                                                              \n"
"  %ifidni %1,di                                                       \n"
"    %assign %$l_n 5                                                   \n"
"  %endif                                                              \n"
"  %ifidni %1,bp                                                       \n"
"    %assign %$l_n 6                                                   \n"
"  %endif                                                              \n"
"  ;%ifidni %1,sp                                                      \n"
"  ;  %assign %$l_n 7                                                  \n"
"  ;%endif                                                             \n"
"                                                                      \n"
"  %if %$l_n == 8                                                      \n"
"    %fatal expected word register (other than sp) in left operand     \n"
"  %endif                                                              \n"
"                                                                      \n"
"  %if %$r_n != 8                                                      \n"
"    ; right operand is register                                       \n"
"    %if %$l_n == 0                                                    \n"
"      ; movsx ax, r                                                   \n"
"      %if %$r_n != 0                                                  \n"
"        mov al, %2                                                    \n"
"      %endif                                                          \n"
"      cbw                                                             \n"
"    %else                                                             \n"
"      %if %$r_n < 4                                                   \n"
"        %assign %$reg %$r_n                                           \n"
"      %else                                                           \n"
"        %assign %$reg %$r_n - 4                                       \n"
"      %endif                                                          \n"
"      %if %$l_n == %$reg                                              \n"
"        ; movsx non-ax, lo/hi of the same non-ax reg                  \n"
"        xchg ax, %1                                                   \n"
"        %if %$r_n >= 4                                                \n"
"          mov al, ah                                                  \n"
"        %endif                                                        \n"
"      %else                                                           \n"
"        ; movsx non-ax, lo/hi of other reg                            \n"
"        mov %1, ax                                                    \n"
"        %if %$r_n != 0                                                \n"
"          mov al, %2                                                  \n"
"        %endif                                                        \n"
"      %endif                                                          \n"
"      cbw                                                             \n"
"      xchg ax, %1                                                     \n"
"    %endif                                                            \n"
"  %else                                                               \n"
"    ; right operand is memory                                         \n"
"    %if %$l_n == 0                                                    \n"
"      ; movsx ax, mem                                                 \n"
"      mov al, %2                                                      \n"
"      cbw                                                             \n"
"    %else                                                             \n"
"      ; movsx non-ax, mem                                             \n"
"      ; Strip leading \"byte\" from %2 for lea.                       \n"
"      %define %$mem %2                                                \n"
"      %defstr %$mem_str %2                                            \n"
"      %substr %$pfx %$mem_str 1,4                                     \n"
"      %ifidni %$pfx, \"byte\"                                         \n"
"        %substr %$pfx %$mem_str 5                                     \n"
"        %ifidn %$pfx, \" \"                                           \n"
"          %substr %$mem_str %$mem_str 5,-1                            \n"
"          %deftok %$mem %$mem_str                                     \n"
"        %endif                                                        \n"
"        %ifidn %$pfx, `\t`                                            \n"
"          %substr %$mem_str %$mem_str 5,-1                            \n"
"          %deftok %$mem %$mem_str                                     \n"
"        %endif                                                        \n"
"        %ifidn %$pfx, \"[\"                                           \n"
"          %substr %$mem_str %$mem_str 5,-1                            \n"
"          %deftok %$mem %$mem_str                                     \n"
"        %endif                                                        \n"
"      %endif                                                          \n"
"      ; TBD: move segment prefix (if any) from \"lea ..., [seg:...]\" \n"
"      ; to \"mov ..., [seg:...]\"                                     \n"
"      lea  %1, %$mem                                                  \n"
"      %if %$l_n != 1                                                  \n"
"        xchg bx, %1                                                   \n"
"      %endif                                                          \n"
"      mov  bl, [bx]                                                   \n"
"      xchg ax, bx                                                     \n"
"      cbw                                                             \n"
"      xchg ax, bx                                                     \n"
"      %if %$l_n != 1                                                  \n"
"        xchg bx, %1                                                   \n"
"      %endif                                                          \n"
"    %endif                                                            \n"
"  %endif                                                              \n"
"                                                                      \n"
"  %pop ; __CTX_MOVSX__                                                \n"
"%endm                                                                 \n"
);
alexfru commented 6 years ago

You'll also need your own non-DOS startup code. Here's a portion of c0ds.asm that you'll need:

bits 16

    extern ___start__
    extern __start__bss
    extern __stop__bss

section .text

    global __start
__start:
    mov ax, ss
    mov ds, ax ; DS=ES=SS in small model .EXEs and in tiny model .COMs
    mov es, ax
    ; Init .bss
    mov di, __start__bss
    mov cx, __stop__bss
    sub cx, di
    xor al, al
    cld
    rep stosb

    jmp ___start__ ; __start__() will set up argc and argv for main() and call exit(main(argc, argv))

section .bss
    ; .bss must exist for __start__bss and __stop__bss to also exist
    resw    1
alexfru commented 6 years ago

This should be enough to get you started.

Oh, btw, you shouldn't probably touch the very first 1KB of physical memory. It is reserved for addresses of interrupt handlers (256 seg:ofs pairs). If you want to use any interrupts, you shouldn't store arbitrary code or data there.

cocus commented 6 years ago

Alex, I tried a simple hello world (which writes in the internal UART of the 80188), without using the printf, nor any stdlib function, but switching from flat to small (exe) as you stated in your test code. After a successfull compilation, I loaded the result file in IDA. I was following along the code as it was the same as in c0ds.asm (I didn't linked it, but it got compiled). After the code detects if it's a .COM or .EXE, it calls ___start__. Here is where I think this code will crash. If you check out the code at c0.c in function ___start__, the first executable line is __DosGetVect(0, __Int00DE); which in fact calls the function __DosGetVect. This function calls an interrupt vector (0x21). Since there is no DOS running here, that interrupt will likely cause a crash (the int is not defined in the lower ram memory). So, I don't know how to continue. Maybe I could modify the c0.c and remove this code. Anyway, I will try that and report what happened. Thanks for your suggestions!

PS: This is the code I was using to test the .exe code (which didn't used any of the fancy things added by using the small memory model, but it was just a test). On a side note, this code runs without any problems if I use the flat model. Note: the file 80c186eb.h only contains the offsets for the peripherals included in this processor (like S0ST, and so on).

#include <stdint.h>
#include "80c186eb.h"
#include "80186.h"

uint8_t rd8_peri(uint8_t addr)
{
    asm("push dx");
    asm("mov dh, 0FFh");
    asm("mov dl, [bp + 4]");
    asm("in al, dx");
    asm("pop dx");  
}

void wr8_peri(uint8_t addr, uint8_t data)
{
    asm("push dx");
    asm("push ax");
    asm("mov dh, 0FFh");
    asm("mov dl, [bp + 4]");
    asm("mov al, [bp + 6]");
    asm("out dx, al");
    asm("pop ax");
    asm("pop dx");      
}

uint8_t cin_stat(void)
{
    return rd8_peri(S0STS);
}
uint8_t cin_buf(void)
{
    return rd8_peri(R0BUF);
}
void cout(uint8_t c)
{
    while((rd8_peri(S0STS) & 8) == 0);
    wr8_peri(T0BUF, c);
}
void puts(char * str)
{
    while(*str)
    {
        cout(*str);
        str++;
    }
}

void main(void)
{
    uint8_t rx;
    puts("\r\nThis is a test!\r\n");
    while(1)
    {
        if ((cin_stat() & 0x40) == 0x40)
        {
            rx = cin_buf();
            cout(rx);

            if (rx == 'Q')
            {
                return;
            }
        }
    }
}
alexfru commented 6 years ago

I don't understand the problem. You know there's no DOS and yet you ignore my suggested alternative startup code (see above) and proceed compiling the code as if for DOS, linking in the standard library for DOS. Why?

Take a look at smlrcc options on the wiki. Around -doss. Can you spot a more appropriate alternative to it?

cocus commented 6 years ago

In your test app code that you gave me earlier, you explictly wrote Compile: smlrcc -doss ldrtst.c -o ldrtst.exe. I was following your exact solution. I should have used -small and not -doss and the startup, but I found out this just now. There is no reason to be mean. I will try again later and report the results.

alexfru commented 6 years ago

I thought the problem was more than obvious. Sorry. A few comments about your "inline" asm code:

cocus commented 6 years ago

Nice to know that about the inline asm. And about the conversion of 8 bits char to a 16, yes, I spotted that. Thanks!

I did some tests, mainly doing the .exe parsing in my host machine and producing a flat image ready to use. In the target I had to copy the .rodata and .data to RAM (where DS points to). I had some trouble finding the true offset to the .data and .rodata since I wanted to copy them assuming they're after the .text section. This is not always true since the compiler padds to align the sections. However I figured it out, so no worries.

However, the last thing that I found was an if expression which ended up using a setne instruction, that is a 386 instruction. I had to do something similar to what you did with movszand movsx. Here is an excerpt of the code (which, btw, its not mine, but I used it as a dirty test) that produces this behavior:

    char i,j;
    char fdd;
[some lines of code, mostly sums and subtracts]
    if ((j)||(fdd))
    {
        fdd=1;
        cout('0' + j);
    }

It produced the following assembly:

; if
; RPN'ized expression: "j fdd || "
; Expanded expression: "(@-4) *(-1) _Bool [sh||->32] (@-8) *(-1) _Bool ||[32] "
; Fused expression:    "*(-1) (@-4) _Bool [sh||->32] *(-1) (@-8) _Bool ||[32]  "
    mov al, [bp-4]
    cbw
    test    ax, ax
    setne   al
    cbw
; JumpIfNotZero
    test    ax, ax
    jne L32
    mov al, [bp-8]
    cbw
    test    ax, ax
    setne   al
    cbw
L32:
; JumpIfZero
    test    ax, ax
    je  L30

Thanks!

alexfru commented 6 years ago

I did have a feeling that I'd missed something. setcc it is, thanks!

Here's adjusted 80186.h:

// File: 80186.h
//
// To compile your code for 80186/80188/80286 include this file
// at the top of every C file.
//
// This is only for the tiny and small memory models of Smaller C
// (i.e. for use with options: -tiny/-dost, -small/-doss).
// Other models use 32-bit registers unavailable on any x86 CPU
// earlier than 80386.
//
// This is for use with NASM only (not YASM or FASM).
asm(
"cpu 186                                                               \n"
"                                                                      \n"
"%define ___ReGs___Lo8___ \"alblcldl\"                                 \n"
"%define ___ReGs___Hi8___ \"ahbhchdh\"                                 \n"
"%define ___ReGs___16___ \"axbxcxdxsidibpsp\"                          \n"
"                                                                      \n"
"%macro movzx 2.nolist                                                 \n"
"  %push __CTX_MOVZX__                                                 \n"
"                                                                      \n"
"  %assign %$r_n 8 ; not 8-bit register                                \n"
"  %ifidni %2,al                                                       \n"
"    %assign %$r_n 0                                                   \n"
"  %endif                                                              \n"
"  %ifidni %2,bl                                                       \n"
"    %assign %$r_n 1                                                   \n"
"  %endif                                                              \n"
"  %ifidni %2,cl                                                       \n"
"    %assign %$r_n 2                                                   \n"
"  %endif                                                              \n"
"  %ifidni %2,dl                                                       \n"
"    %assign %$r_n 3                                                   \n"
"  %endif                                                              \n"
"  %ifidni %2,ah                                                       \n"
"    %assign %$r_n 4                                                   \n"
"  %endif                                                              \n"
"  %ifidni %2,bh                                                       \n"
"    %assign %$r_n 5                                                   \n"
"  %endif                                                              \n"
"  %ifidni %2,ch                                                       \n"
"    %assign %$r_n 6                                                   \n"
"  %endif                                                              \n"
"  %ifidni %2,dh                                                       \n"
"    %assign %$r_n 7                                                   \n"
"  %endif                                                              \n"
"                                                                      \n"
"  %assign %$l_n 8 ; not 16-bit register                               \n"
"  %ifidni %1,ax                                                       \n"
"    %assign %$l_n 0                                                   \n"
"  %endif                                                              \n"
"  %ifidni %1,bx                                                       \n"
"    %assign %$l_n 1                                                   \n"
"  %endif                                                              \n"
"  %ifidni %1,cx                                                       \n"
"    %assign %$l_n 2                                                   \n"
"  %endif                                                              \n"
"  %ifidni %1,dx                                                       \n"
"    %assign %$l_n 3                                                   \n"
"  %endif                                                              \n"
"  %ifidni %1,si                                                       \n"
"    %assign %$l_n 4                                                   \n"
"  %endif                                                              \n"
"  %ifidni %1,di                                                       \n"
"    %assign %$l_n 5                                                   \n"
"  %endif                                                              \n"
"  %ifidni %1,bp                                                       \n"
"    %assign %$l_n 6                                                   \n"
"  %endif                                                              \n"
"  ;%ifidni %1,sp                                                      \n"
"  ;  %assign %$l_n 7                                                  \n"
"  ;%endif                                                             \n"
"                                                                      \n"
"  %if %$l_n == 8                                                      \n"
"    %fatal expected word register (other than sp) in left operand     \n"
"  %endif                                                              \n"
"                                                                      \n"
"  %if %$r_n != 8                                                      \n"
"    ; right operand is register                                       \n"
"    %if %$l_n < 4                                                     \n"
"      ; movzx hi_lo, reg                                              \n"
"      %if %$l_n != %$r_n                                              \n"
"        %substr %$reg_lo_str ___ReGs___Lo8___ (%$l_n*2+1), 2          \n"
"                           ; 2-char string with reg name, e.g. \"al\" \n"
"        %deftok %$reg_lo %$reg_lo_str ; tokenized to e.g. al          \n"
"        mov %$reg_lo, %2                                              \n"
"      %endif                                                          \n"
"      %substr %$reg_hi_str ___ReGs___Hi8___ (%$l_n*2+1), 2            \n"
"      %deftok %$reg_hi %$reg_hi_str                                   \n"
"      mov %$reg_hi, 0                                                 \n"
"    %else                                                             \n"
"      ; movzx non-hi_lo, reg                                          \n"
"      mov %1, ax                                                      \n"
"      %if %$r_n != 0                                                  \n"
"        mov al, %2                                                    \n"
"      %endif                                                          \n"
"      mov ah, 0                                                       \n"
"      xchg ax, %1                                                     \n"
"    %endif                                                            \n"
"  %else                                                               \n"
"    ; right operand is memory                                         \n"
"    %if %$l_n < 4                                                     \n"
"      ; movzx hi_lo, mem                                              \n"
"      %substr %$reg_lo_str ___ReGs___Lo8___ (%$l_n*2+1), 2            \n"
"      %deftok %$reg_lo %$reg_lo_str                                   \n"
"      %substr %$reg_hi_str ___ReGs___Hi8___ (%$l_n*2+1), 2            \n"
"      %deftok %$reg_hi %$reg_hi_str                                   \n"
"      mov %$reg_lo, %2                                                \n"
"      mov %$reg_hi, 0                                                 \n"
"    %else                                                             \n"
"      ; movzx non-hi_lo, mem                                          \n"
"      ; Strip leading \"byte\" from %2 for lea.                       \n"
"      %define %$mem %2                                                \n"
"      %defstr %$mem_str %2                                            \n"
"      %substr %$pfx %$mem_str 1,4                                     \n"
"      %ifidni %$pfx, \"byte\"                                         \n"
"        %substr %$pfx %$mem_str 5                                     \n"
"        %ifidn %$pfx, \" \"                                           \n"
"          %substr %$mem_str %$mem_str 5,-1                            \n"
"          %deftok %$mem %$mem_str                                     \n"
"        %endif                                                        \n"
"        %ifidn %$pfx, `\t`                                            \n"
"          %substr %$mem_str %$mem_str 5,-1                            \n"
"          %deftok %$mem %$mem_str                                     \n"
"        %endif                                                        \n"
"        %ifidn %$pfx, \"[\"                                           \n"
"          %substr %$mem_str %$mem_str 5,-1                            \n"
"          %deftok %$mem %$mem_str                                     \n"
"        %endif                                                        \n"
"      %endif                                                          \n"
"      ; TBD: move segment prefix (if any) from \"lea ..., [seg:...]\" \n"
"      ; to \"mov ..., [seg:...]\"                                     \n"
"      lea  %1, %$mem                                                  \n"
"      xchg bx, %1                                                     \n"
"      mov  bl, [bx]                                                   \n"
"      mov  bh, 0                                                      \n"
"      xchg bx, %1                                                     \n"
"    %endif                                                            \n"
"  %endif                                                              \n"
"                                                                      \n"
"  %pop ; __CTX_MOVZX__                                                \n"
"%endm                                                                 \n"
"                                                                      \n"
"%macro movsx 2.nolist                                                 \n"
"  %push __CTX_MOVSX__                                                 \n"
"                                                                      \n"
"  %assign %$r_n 8 ; not 8-bit register                                \n"
"  %ifidni %2,al                                                       \n"
"    %assign %$r_n 0                                                   \n"
"  %endif                                                              \n"
"  %ifidni %2,bl                                                       \n"
"    %assign %$r_n 1                                                   \n"
"  %endif                                                              \n"
"  %ifidni %2,cl                                                       \n"
"    %assign %$r_n 2                                                   \n"
"  %endif                                                              \n"
"  %ifidni %2,dl                                                       \n"
"    %assign %$r_n 3                                                   \n"
"  %endif                                                              \n"
"  %ifidni %2,ah                                                       \n"
"    %assign %$r_n 4                                                   \n"
"  %endif                                                              \n"
"  %ifidni %2,bh                                                       \n"
"    %assign %$r_n 5                                                   \n"
"  %endif                                                              \n"
"  %ifidni %2,ch                                                       \n"
"    %assign %$r_n 6                                                   \n"
"  %endif                                                              \n"
"  %ifidni %2,dh                                                       \n"
"    %assign %$r_n 7                                                   \n"
"  %endif                                                              \n"
"                                                                      \n"
"  %assign %$l_n 8 ; not 16-bit register                               \n"
"  %ifidni %1,ax                                                       \n"
"    %assign %$l_n 0                                                   \n"
"  %endif                                                              \n"
"  %ifidni %1,bx                                                       \n"
"    %assign %$l_n 1                                                   \n"
"  %endif                                                              \n"
"  %ifidni %1,cx                                                       \n"
"    %assign %$l_n 2                                                   \n"
"  %endif                                                              \n"
"  %ifidni %1,dx                                                       \n"
"    %assign %$l_n 3                                                   \n"
"  %endif                                                              \n"
"  %ifidni %1,si                                                       \n"
"    %assign %$l_n 4                                                   \n"
"  %endif                                                              \n"
"  %ifidni %1,di                                                       \n"
"    %assign %$l_n 5                                                   \n"
"  %endif                                                              \n"
"  %ifidni %1,bp                                                       \n"
"    %assign %$l_n 6                                                   \n"
"  %endif                                                              \n"
"  ;%ifidni %1,sp                                                      \n"
"  ;  %assign %$l_n 7                                                  \n"
"  ;%endif                                                             \n"
"                                                                      \n"
"  %if %$l_n == 8                                                      \n"
"    %fatal expected word register (other than sp) in left operand     \n"
"  %endif                                                              \n"
"                                                                      \n"
"  %if %$r_n != 8                                                      \n"
"    ; right operand is register                                       \n"
"    %if %$l_n == 0                                                    \n"
"      ; movsx ax, r                                                   \n"
"      %if %$r_n != 0                                                  \n"
"        mov al, %2                                                    \n"
"      %endif                                                          \n"
"      cbw                                                             \n"
"    %else                                                             \n"
"      %if %$r_n < 4                                                   \n"
"        %assign %$reg %$r_n                                           \n"
"      %else                                                           \n"
"        %assign %$reg %$r_n - 4                                       \n"
"      %endif                                                          \n"
"      %if %$l_n == %$reg                                              \n"
"        ; movsx non-ax, lo/hi of the same non-ax reg                  \n"
"        xchg ax, %1                                                   \n"
"        %if %$r_n >= 4                                                \n"
"          mov al, ah                                                  \n"
"        %endif                                                        \n"
"      %else                                                           \n"
"        ; movsx non-ax, lo/hi of other reg                            \n"
"        mov %1, ax                                                    \n"
"        %if %$r_n != 0                                                \n"
"          mov al, %2                                                  \n"
"        %endif                                                        \n"
"      %endif                                                          \n"
"      cbw                                                             \n"
"      xchg ax, %1                                                     \n"
"    %endif                                                            \n"
"  %else                                                               \n"
"    ; right operand is memory                                         \n"
"    %if %$l_n == 0                                                    \n"
"      ; movsx ax, mem                                                 \n"
"      mov al, %2                                                      \n"
"      cbw                                                             \n"
"    %else                                                             \n"
"      ; movsx non-ax, mem                                             \n"
"      ; Strip leading \"byte\" from %2 for lea.                       \n"
"      %define %$mem %2                                                \n"
"      %defstr %$mem_str %2                                            \n"
"      %substr %$pfx %$mem_str 1,4                                     \n"
"      %ifidni %$pfx, \"byte\"                                         \n"
"        %substr %$pfx %$mem_str 5                                     \n"
"        %ifidn %$pfx, \" \"                                           \n"
"          %substr %$mem_str %$mem_str 5,-1                            \n"
"          %deftok %$mem %$mem_str                                     \n"
"        %endif                                                        \n"
"        %ifidn %$pfx, `\t`                                            \n"
"          %substr %$mem_str %$mem_str 5,-1                            \n"
"          %deftok %$mem %$mem_str                                     \n"
"        %endif                                                        \n"
"        %ifidn %$pfx, \"[\"                                           \n"
"          %substr %$mem_str %$mem_str 5,-1                            \n"
"          %deftok %$mem %$mem_str                                     \n"
"        %endif                                                        \n"
"      %endif                                                          \n"
"      ; TBD: move segment prefix (if any) from \"lea ..., [seg:...]\" \n"
"      ; to \"mov ..., [seg:...]\"                                     \n"
"      lea  %1, %$mem                                                  \n"
"      %if %$l_n != 1                                                  \n"
"        xchg bx, %1                                                   \n"
"      %endif                                                          \n"
"      mov  bl, [bx]                                                   \n"
"      xchg ax, bx                                                     \n"
"      cbw                                                             \n"
"      xchg ax, bx                                                     \n"
"      %if %$l_n != 1                                                  \n"
"        xchg bx, %1                                                   \n"
"      %endif                                                          \n"
"    %endif                                                            \n"
"  %endif                                                              \n"
"                                                                      \n"
"  %pop ; __CTX_MOVSX__                                                \n"
"%endm                                                                 \n"
"                                                                      \n"
"%macro ___set_cc___ 2.nolist                                          \n"
"  mov %2, byte 0                                                      \n"
"  j%-1 %%skip                                                         \n"
"  mov %2, byte 1                                                      \n"
"%%skip:                                                               \n"
"%endm                                                                 \n"
"                                                                      \n"
"%macro seta 1.nolist                                                  \n"
"  ___set_cc___ a, %1                                                  \n"
"%endm                                                                 \n"
"%macro setae 1.nolist                                                 \n"
"  ___set_cc___ ae, %1                                                 \n"
"%endm                                                                 \n"
"%macro setb 1.nolist                                                  \n"
"  ___set_cc___ b, %1                                                  \n"
"%endm                                                                 \n"
"%macro setbe 1.nolist                                                 \n"
"  ___set_cc___ be, %1                                                 \n"
"%endm                                                                 \n"
"%macro setc 1.nolist                                                  \n"
"  ___set_cc___ c, %1                                                  \n"
"%endm                                                                 \n"
"%macro sete 1.nolist                                                  \n"
"  ___set_cc___ e, %1                                                  \n"
"%endm                                                                 \n"
"%macro setg 1.nolist                                                  \n"
"  ___set_cc___ g, %1                                                  \n"
"%endm                                                                 \n"
"%macro setge 1.nolist                                                 \n"
"  ___set_cc___ ge, %1                                                 \n"
"%endm                                                                 \n"
"%macro setl 1.nolist                                                  \n"
"  ___set_cc___ l, %1                                                  \n"
"%endm                                                                 \n"
"%macro setle 1.nolist                                                 \n"
"  ___set_cc___ le, %1                                                 \n"
"%endm                                                                 \n"
"%macro setna 1.nolist                                                 \n"
"  ___set_cc___ na, %1                                                 \n"
"%endm                                                                 \n"
"%macro setnae 1.nolist                                                \n"
"  ___set_cc___ nae, %1                                                \n"
"%endm                                                                 \n"
"%macro setnb 1.nolist                                                 \n"
"  ___set_cc___ nb, %1                                                 \n"
"%endm                                                                 \n"
"%macro setnbe 1.nolist                                                \n"
"  ___set_cc___ nbe, %1                                                \n"
"%endm                                                                 \n"
"%macro setnc 1.nolist                                                 \n"
"  ___set_cc___ nc, %1                                                 \n"
"%endm                                                                 \n"
"%macro setne 1.nolist                                                 \n"
"  ___set_cc___ ne, %1                                                 \n"
"%endm                                                                 \n"
"%macro setng 1.nolist                                                 \n"
"  ___set_cc___ ng, %1                                                 \n"
"%endm                                                                 \n"
"%macro setnge 1.nolist                                                \n"
"  ___set_cc___ nge, %1                                                \n"
"%endm                                                                 \n"
"%macro setnl 1.nolist                                                 \n"
"  ___set_cc___ nl, %1                                                 \n"
"%endm                                                                 \n"
"%macro setnle 1.nolist                                                \n"
"  ___set_cc___ nle, %1                                                \n"
"%endm                                                                 \n"
"%macro setno 1.nolist                                                 \n"
"  ___set_cc___ no, %1                                                 \n"
"%endm                                                                 \n"
"%macro setnp 1.nolist                                                 \n"
"  ___set_cc___ np, %1                                                 \n"
"%endm                                                                 \n"
"%macro setns 1.nolist                                                 \n"
"  ___set_cc___ ns, %1                                                 \n"
"%endm                                                                 \n"
"%macro setnz 1.nolist                                                 \n"
"  ___set_cc___ nz, %1                                                 \n"
"%endm                                                                 \n"
"%macro seto 1.nolist                                                  \n"
"  ___set_cc___ o, %1                                                  \n"
"%endm                                                                 \n"
"%macro setp 1.nolist                                                  \n"
"  ___set_cc___ p, %1                                                  \n"
"%endm                                                                 \n"
"%macro setpe 1.nolist                                                 \n"
"  ___set_cc___ pe, %1                                                 \n"
"%endm                                                                 \n"
"%macro setpo 1.nolist                                                 \n"
"  ___set_cc___ po, %1                                                 \n"
"%endm                                                                 \n"
"%macro sets 1.nolist                                                  \n"
"  ___set_cc___ s, %1                                                  \n"
"%endm                                                                 \n"
"%macro setz 1.nolist                                                  \n"
"  ___set_cc___ z, %1                                                  \n"
"%endm                                                                 \n"
);
alexfru commented 6 years ago

The loader that I provided was supposed to clear things up. I hope it did help.

cocus commented 6 years ago

Nice macro! About the loader, yes, it does clear the .bss, but I added some additional things that are related to the way I load the .exe files. It works quite well. If I have something else I'll post it in another issue. I think this one is solved. Thanks for your support!

alexfru commented 6 years ago

I've included 80186.h (under include/sys). Closing.