├── LICENSE ├── README.md ├── am9511.bas ├── am9511.c ├── am9511.h ├── ansi.h ├── build ├── cnvtest.c ├── cnvtest.com ├── floatcnv.c ├── floatcnv.h ├── getopt.3 ├── getopt.c ├── getopt.h ├── howto.txt ├── hw9511.c ├── ova.c ├── ova.h ├── planet32.com ├── planeta.com ├── test.c ├── test.com ├── test14.com ├── test58.com ├── test912.com ├── testhw.com ├── testhw14.com ├── testhw58.com ├── testhw912.com └── types.h /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Fred Weigel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # am9511 2 | AM9511 floating point chip emulator. Includes a library to allow conversion of 32 bit floating point formats. AM9511, 3 | Microsoft, IEEE, Hi-Tech C formats are supported. 4 | 5 | This is designed to run both on a Z80, or on a generic sytem with gcc (or tcc). floatcnv works on both Z80 and hosting 6 | platforms. am9511 is primarily to be integrated into emulation platforms. howto.txt has some instructions for integrating 7 | into Zxc and RunCPM. 8 | 9 | Emulation of AM9511 is now feature complete -- can be integrated. NOTE - this is beta only. If integrated, or using 10 | live chip, AM9511.BAS is MBASIC test program that can drive both the emulator or the actual chip. 11 | 12 | Some sample reference floating values are given for tests for floatcnv. 13 | 14 | We are mapping AM9511 functionality into the host. This part (the AM9511) could be used with 8080, Z80, 8085, 6800, 15 | z8000, and even Apple 2 (6502) systems, providing 16 and 32 bit integer and 32 bit floating point. After validation 16 | with the native host floating point, I intend on providing alternate implementations that mirror the actual AM9511 17 | chip implementation (future project). 18 | 19 | test.com/test14.com/test58.com/test912.com is a basic test of the emulator. These are also linked as testhw.com/testhw14.com, 20 | testhw58.com and testhw912.com to go straight to hardware. 21 | 22 | testhw.com (14 and 58) is the same as test.com, but runs on the actual chip: 23 | 24 | testhw -d port -s port 25 | -d port sets data port for the am9511 26 | -s port sets status/command port for the am9511 27 | 28 | both port values must be in decimal. Typical values would be 80 and 81. 29 | 30 | ova.c implements integer 16 and 32 bit arithmetic, with overflow. 31 | 32 | am9511 is now in testing phase. All features are in, but not extensively tested. 33 | 34 | getopt.c is the BSD getopt() function, because HI-TECH C doesn't have it. 35 | 36 | HI-TECH C is not "fully" ANSI C 89 compatible. "ansi.h" defines signed, 37 | const, volatile. Can't blame HI-TECH C 3.09 -- the standard was two years 38 | AFTER this compiler. 39 | -------------------------------------------------------------------------------- /am9511.bas: -------------------------------------------------------------------------------- 1 | 1000 REM AM9511.BAS 2 | 1010 REM 3 | 1020 REM INTERFACE WITH AM9511 CHIP (EMULATOR) 4 | 1030 REM THIS IS *NOT* PRODUCTION CODE -- JUST PROOF OF CONCEPT 5 | 1040 REM THIS SHOULD BE ENOUGH TO WRITE A TEST SEQUENCE FOR BOTH 6 | 1050 REM EMULATOR AND ACTUAL CHIP 7 | 1060 REM 8 | 1070 GOTO 1130 9 | 1080 GOSUB 1660 ' AM9511 -> X 10 | 1090 GOSUB 1430 ' X -> AM9511 11 | 1100 GOSUB 1370 ' WAIT FOR NOT BUSY 12 | 1110 GOSUB 1580 ' PUSH 0D/0.0 13 | 1120 GOSUB 1620 ' PUSH 0S 14 | 1130 AM.STATUS = &H51 15 | 1140 AM.DATA = &H50 16 | 1150 X = 0! 17 | 1160 REM 18 | 1170 REM MAIN 19 | 1180 GOSUB 1370 20 | 1190 OUT AM.STATUS,&H0 ' NOP 21 | 1200 GOSUB 1370 22 | 1210 S = INP(AM.STATUS) : PRINT "NOP STATUS = "; HEX$(S) 23 | 1220 OUT AM.STATUS,&H1A ' PUPI 24 | 1230 GOSUB 1370 25 | 1240 S = INP(AM.STATUS) : PRINT "PUPI STATUS = "; HEX$(S) 26 | 1250 GOSUB 1660 : PRINT " "; X 27 | 1260 Y=X ' TRY SENDING PI BACK TO CHIP AND TESTING 28 | 1270 REM WE CAN TRY TO "ROUND-TRIP" PI. WE USED PUPI TO GET THE 29 | 1280 REM VALUE INTO THE CHIP, THEN READ IT OUT, AND CONVERTED TO MS. 30 | 1290 REM WE SEND THAT VALUE BACK TO THE CHIP, THEN READ IT AGAIN 31 | 1300 REM THE VALUE OF "PUPI" CAN BE CONVERTED TO AND FROM MS WITH 32 | 1310 REM NO LOSS. 33 | 1320 GOSUB 1430 34 | 1330 GOSUB 1660 : PRINT Y, X 35 | 1331 X=10.5 : Z=3.1 : PRINT "BEGIN" 36 | 1332 FOR I=1 TO 10000:Z=X*Y:NEXT 37 | 1333 PRINT "END" 38 | 1340 STOP 39 | 1350 REM WAIT FOR AM9511 NOT BUSY 40 | 1360 REM 41 | 1370 WAIT AM.STATUS,&H80,&H80 42 | 1380 RETURN 43 | 1390 REM PUSH MS FLOAT TO AM9511 STACK 44 | 1400 REM 45 | 1410 REM INCOMING FLOAT IS IN VARIABLE X 46 | 1420 REM 47 | 1430 P = VARPTR(X) 48 | 1440 IF PEEK(P + 3) = 0 THEN 1580 49 | 1450 S = PEEK(P + 2) AND &H80 50 | 1460 E = PEEK(P + 3) - 128 51 | 1470 M.H = PEEK(P + 2) OR &H80 52 | 1480 M.LL = PEEK(P + 0) 53 | 1490 M.LH = PEEK(P + 1) 54 | 1500 E = (E AND &H7F) OR S 55 | 1510 OUT AM.DATA,M.LL 56 | 1520 OUT AM.DATA,M.LH 57 | 1530 OUT AM.DATA,M.H 58 | 1540 OUT AM.DATA,E 59 | 1550 RETURN 60 | 1560 REM 61 | 1570 REM WRITE 00 00 00 00 (ZERO) TO CHIP 62 | 1580 OUT AM.DATA,0 63 | 1590 OUT AM.DATA,0 64 | 1600 REM 65 | 1610 REM WRITE 0 (ZERO) TO CHIP 66 | 1620 OUT AM.DATA,0 67 | 1630 OUT AM.DATA,0 68 | 1640 RETURN 69 | 1650 REM POP FLOAT FROM AM9511 STACK, RETURN IN X 70 | 1660 B3 = INP(AM.DATA) 71 | 1670 B2 = INP(AM.DATA) 72 | 1680 B1 = INP(AM.DATA) 73 | 1690 B0 = INP(AM.DATA) 74 | 1700 REM PRINT HEX$(B0),HEX$(B1),HEX$(B2),HEX$(B3) 75 | 1710 X = 0 76 | 1720 IF (B2 AND &H80) = 0 THEN RETURN 77 | 1730 S = B3 AND &H80 78 | 1740 E = B3 AND &H7F 79 | 1750 M.H = B2 80 | 1760 M.LL = B0 81 | 1770 M.LH = B1 82 | 1780 E = E + 128 83 | 1790 M.H = M.H AND &H7F 84 | 1800 M.H = M.H OR S 85 | 1810 P = VARPTR(X) 86 | 1820 POKE P + 0,M.LL 87 | 1830 POKE P + 1,M.LH 88 | 1840 POKE P + 2,M.H 89 | 1850 POKE P + 3,E 90 | 1860 RETURN 91 | M.H OR S 92 | 1810 P = VARPTR(X) 93 | 18 94 | -------------------------------------------------------------------------------- /am9511.c: -------------------------------------------------------------------------------- 1 | /* am9511.c 2 | * 3 | * First cut am9511 emulation. This version is NOT cycle accurate, 4 | * or even algorithm accurate. It should be a somewhat reasonable 5 | * stand-in, which should allow us to run base-line comparisions with 6 | * the real device. 7 | */ 8 | 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include "am9511.h" 15 | #include "floatcnv.h" 16 | #include "ova.h" 17 | #include "types.h" 18 | 19 | 20 | /* Define fp_na() -- fp to native and 21 | * na_fp() -- native to fp 22 | */ 23 | #ifdef z80 24 | #define fp_na(x,y) fp_hi(x,y) 25 | #define na_fp(x,y) hi_fp(x,y) 26 | #else 27 | #define fp_na(x,y) fp_ie(x,y) 28 | #define na_fp(x,y) ie_fp(x,y) 29 | #endif 30 | 31 | /* Stack is 16 bytes long. sp is the stack pointer. 32 | * Points to next location to use. 33 | * 34 | * AM9511 status and operator latch 35 | */ 36 | 37 | struct am_context { 38 | unsigned char stack[16]; 39 | int sp; 40 | void *fptmp; 41 | unsigned char status; 42 | unsigned char op_latch; 43 | #ifndef NDEBUG 44 | unsigned char last_latch; 45 | #endif 46 | }; 47 | 48 | 49 | #define AM_OP 0x1f 50 | 51 | 52 | /* Add to sp 53 | */ 54 | #define sp_add(n) ((ctx->sp + (n)) & 0xf) 55 | 56 | 57 | /* Return pointer into stack 58 | */ 59 | #define stpos(offset) (ctx->stack + sp_add(offset)) 60 | 61 | 62 | /* Increment stack pointer 63 | */ 64 | #define inc_sp(n) ctx->sp = sp_add(n) 65 | 66 | 67 | /* Decrement stack pointer 68 | */ 69 | #define dec_sp(n) ctx->sp = sp_add(-(n)) 70 | 71 | 72 | /* Push byte to am9511 stack 73 | */ 74 | void am_push(void *amp, unsigned char v) { 75 | struct am_context *ctx = (struct am_context *)amp; 76 | *stpos(0) = v; 77 | inc_sp(1); 78 | } 79 | 80 | 81 | /* Pop byte from am9511 stack 82 | */ 83 | unsigned char am_pop(void *amp) { 84 | struct am_context *ctx = (struct am_context *)amp; 85 | dec_sp(1); 86 | return *stpos(0); 87 | } 88 | 89 | 90 | 91 | /* Return status of am9511 92 | */ 93 | unsigned char am_status(void *amp) { 94 | struct am_context *ctx = (struct am_context *)amp; 95 | return ctx->status; 96 | } 97 | 98 | 99 | #define IS_SINGLE ((ctx->op_latch & AM_SINGLE) == AM_SINGLE) 100 | #define IS_FIXED (ctx->op_latch & AM_FIXED) 101 | 102 | 103 | /* Set SIGN and ZERO according to op type and top of stack. 104 | * Zero detect for integer is or'ing together all the bytes. 105 | * Zero detect for float is testing bit 23 for 0. 106 | * The sign bit for all types is the top-most bit. If 1 then 107 | * negative. 108 | */ 109 | static void sz(struct am_context *ctx) { 110 | if (IS_SINGLE) { 111 | if ((*stpos(-1) | *stpos(-2)) == 0) 112 | ctx->status |= AM_ZERO; 113 | } else if (IS_FIXED) { 114 | if ((*stpos(-1) | *stpos(-2) | *stpos(-3) | *stpos(-4)) == 0) 115 | ctx->status |= AM_ZERO; 116 | } else { 117 | if ((*stpos(-2) & 0x80) == 0) 118 | ctx->status |= AM_ZERO; 119 | } 120 | if (*stpos(-1) & 0x80) 121 | ctx->status |= AM_SIGN; 122 | } 123 | 124 | 125 | /* PUPI 126 | */ 127 | static void pupi(struct am_context *ctx) { 128 | am_push(ctx, 0xda); /* little end to big end */ 129 | am_push(ctx, 0x0f); 130 | am_push(ctx, 0xc9); 131 | am_push(ctx, 0x02); 132 | sz(ctx); 133 | } 134 | 135 | 136 | /* PTOS PTOD PTOF 137 | * 138 | * This relies on the stack data not moving during a push. 139 | */ 140 | static void pto(struct am_context *ctx) { 141 | unsigned char *s; 142 | 143 | if (IS_SINGLE) { 144 | s = stpos(-2); 145 | am_push(ctx, *s++); 146 | am_push(ctx, *s); 147 | } else { 148 | s = stpos(-4); 149 | am_push(ctx, *s++); 150 | am_push(ctx, *s++); 151 | am_push(ctx, *s++); 152 | am_push(ctx, *s); 153 | } 154 | sz(ctx); 155 | } 156 | 157 | 158 | /* POPS POPD POPF 159 | * 160 | * Note that the SIGN and ZERO flags are set from the element that 161 | * is next on stack. But... it may be wrong! We do not know what the 162 | * new tos element really is! (in terms of type) 163 | * The guide states and SIGN and ZERO are affected, but no more than that. 164 | */ 165 | static void pop(struct am_context *ctx) { 166 | if (IS_SINGLE) 167 | dec_sp(2); 168 | else 169 | dec_sp(4); 170 | sz(ctx); 171 | } 172 | 173 | 174 | /* XCHS XCHD XCHF 175 | */ 176 | static void xch(struct am_context *ctx) { 177 | unsigned char *s, *t, v; 178 | 179 | if (IS_SINGLE) { 180 | s = stpos(-2); 181 | t = stpos(-4); 182 | v = *t; *t++ = *s; *s++ = v; 183 | v = *t; *t = *s; *s = v; 184 | } else { 185 | s = stpos(-4); 186 | t = stpos(-8); 187 | v = *t; *t++ = *s; *s++ = v; 188 | v = *t; *t++ = *s; *s++ = v; 189 | v = *t; *t++ = *s; *s++ = v; 190 | v = *t; *t = *s; *s = v; 191 | } 192 | sz(ctx); 193 | } 194 | 195 | 196 | /* CHSF 197 | */ 198 | static void chsf(struct am_context *ctx) { 199 | /* Floating point sign change - only flip sign 200 | * (if not zero). And, as with the AM9511 chip, CHSF 201 | * is even faster than CHSS. 202 | */ 203 | if (*stpos(-2) & 0x80) 204 | *stpos(-1) ^= 0x80; 205 | sz(ctx); 206 | } 207 | 208 | 209 | /* CHSS CHSD 210 | */ 211 | static void chs(struct am_context *ctx) { 212 | if (IS_SINGLE) { 213 | if (cm16(stpos(-2), stpos(-2))) 214 | ctx->status |= AM_ERR_OVF; 215 | } else { 216 | if (cm32(stpos(-4), stpos(-4))) 217 | ctx->status |= AM_ERR_OVF; 218 | } 219 | sz(ctx); 220 | } 221 | 222 | 223 | /* Push float to stack, set SIGN and ZERO 224 | */ 225 | static void push_float(struct am_context *ctx, float x) { 226 | unsigned char v[4]; 227 | 228 | na_fp(&x, ctx->fptmp); 229 | fp_am(ctx->fptmp, v); 230 | am_push(ctx, v[0]); 231 | am_push(ctx, v[1]); 232 | am_push(ctx, v[2]); 233 | am_push(ctx, v[3]); 234 | ctx->op_latch = AM_FLOAT; 235 | sz(ctx); 236 | } 237 | 238 | 239 | /* FLTS 240 | */ 241 | static void flts(struct am_context *ctx) { 242 | int16 n; 243 | float x; 244 | 245 | n = am_pop(ctx); 246 | n = (n << 8) | am_pop(ctx); 247 | x = n; 248 | push_float(ctx, x); 249 | } 250 | 251 | 252 | /* FLTD 253 | */ 254 | static void fltd(struct am_context *ctx) { 255 | int32 n; 256 | float x; 257 | int b; 258 | 259 | /* HI-TECH C long shift bug 260 | */ 261 | b = am_pop(ctx); 262 | n = b; 263 | 264 | n = n << 8; 265 | b = am_pop(ctx); 266 | n = n | b; 267 | 268 | n = n << 8; 269 | b = am_pop(ctx); 270 | n = n | b; 271 | 272 | n = n << 8; 273 | b = am_pop(ctx); 274 | n = n | b; 275 | 276 | x = n; 277 | push_float(ctx, x); 278 | } 279 | 280 | 281 | /* FIXS 282 | */ 283 | static void fixs(struct am_context *ctx) { 284 | float x; 285 | unsigned char *s; 286 | int n; 287 | 288 | s = stpos(-4); 289 | am_fp(s, ctx->fptmp); 290 | fp_na(ctx->fptmp, &x); 291 | if ((x < -32768.0) || (x > 32767.0)) { 292 | ctx->status |= AM_ERR_OVF; 293 | sz(ctx); 294 | return; 295 | } 296 | dec_sp(4); 297 | n = (int)x; 298 | am_push(ctx, n); 299 | am_push(ctx, n >> 8); 300 | ctx->op_latch = AM_SINGLE; 301 | sz(ctx); 302 | 303 | } 304 | 305 | 306 | /* FIXD 307 | */ 308 | static void fixd(struct am_context *ctx) { 309 | float x; 310 | unsigned char *s; 311 | int32 n; 312 | float xl, xh; 313 | 314 | s = stpos(-4); 315 | am_fp(s, ctx->fptmp); 316 | fp_na(ctx->fptmp, &x); 317 | n = -2147483648; 318 | xl = (float)n; 319 | n = 2147483647; 320 | xh = (float)n; 321 | if ((x < xl) || (x > xh)) { 322 | ctx->status |= AM_ERR_OVF; 323 | sz(ctx); 324 | return; 325 | } 326 | dec_sp(4); 327 | n = (int32)x; 328 | am_push(ctx, n); 329 | am_push(ctx, n >> 8); 330 | am_push(ctx, n >> 16); 331 | am_push(ctx, n >> 24); 332 | ctx->op_latch = AM_DOUBLE; 333 | sz(ctx); 334 | } 335 | 336 | 337 | /* SADD DADD 338 | */ 339 | static void add(struct am_context *ctx) { 340 | int carry; 341 | int overflow; 342 | 343 | if (IS_SINGLE) { 344 | carry = add16( stpos(-4), stpos(-2), stpos(-4)); 345 | overflow = oadd16(stpos(-4), stpos(-2), stpos(-4)); 346 | dec_sp(2); 347 | } else { 348 | carry = add32( stpos(-8), stpos(-4), stpos(-8)); 349 | overflow = oadd32(stpos(-8), stpos(-4), stpos(-8)); 350 | dec_sp(4); 351 | } 352 | if (carry) 353 | ctx->status |= AM_CARRY; 354 | if (overflow) 355 | ctx->status |= AM_ERR_OVF; 356 | sz(ctx); 357 | } 358 | 359 | 360 | /* SSUB DSUB 361 | */ 362 | static void sub(struct am_context *ctx) { 363 | int carry; 364 | int overflow; 365 | 366 | if (IS_SINGLE) { 367 | carry = sub16( stpos(-4), stpos(-2), stpos(-4)); 368 | overflow = osub16(stpos(-4), stpos(-2), stpos(-4)); 369 | dec_sp(2); 370 | } else { 371 | carry = sub32( stpos(-8), stpos(-4), stpos(-8)); 372 | overflow = osub32(stpos(-8), stpos(-4), stpos(-8)); 373 | dec_sp(4); 374 | } 375 | if (carry) 376 | ctx->status |= AM_CARRY; 377 | if (overflow) 378 | ctx->status |= AM_ERR_OVF; 379 | sz(ctx); 380 | } 381 | 382 | 383 | /* MUL 384 | */ 385 | static void mul(struct am_context *ctx) { 386 | int overflow; 387 | 388 | if (IS_SINGLE) { 389 | overflow = mull16(stpos(-4), stpos(-2), stpos(-4)); 390 | dec_sp(2); 391 | } else { 392 | overflow = mull32(stpos(-8), stpos(-4), stpos(-8)); 393 | dec_sp(4); 394 | } 395 | if (overflow) 396 | ctx->status |= AM_ERR_OVF; 397 | sz(ctx); 398 | } 399 | 400 | 401 | /* MUU 402 | */ 403 | static void muu(struct am_context *ctx) { 404 | int overflow; 405 | 406 | if (IS_SINGLE) { 407 | overflow = mulu16(stpos(-4), stpos(-2), stpos(-4)); 408 | dec_sp(2); 409 | } else { 410 | overflow = mulu32(stpos(-8), stpos(-4), stpos(-8)); 411 | dec_sp(4); 412 | } 413 | if (overflow) 414 | ctx->status |= AM_ERR_OVF; 415 | sz(ctx); 416 | } 417 | 418 | 419 | /* DIV 420 | */ 421 | static void divi(struct am_context *ctx) { 422 | int div0; 423 | 424 | if (IS_SINGLE) { 425 | div0 = div16(stpos(-4), stpos(-2), stpos(-4)); 426 | dec_sp(2); 427 | } else { 428 | div0 = div32(stpos(-8), stpos(-4), stpos(-8)); 429 | dec_sp(4); 430 | } 431 | if (div0) 432 | ctx->status |= AM_ERR_DIV0; 433 | sz(ctx); 434 | } 435 | 436 | 437 | /* Detect and report float overflow/underflow 438 | */ 439 | static int fov(struct am_context *ctx, double r) { 440 | int e; 441 | 442 | frexp(r, &e); 443 | if (e > 63) { 444 | ctx->status |= AM_ERR_OVF; 445 | return 1; 446 | } else if (e < -64) { 447 | ctx->status |= AM_ERR_UND; 448 | return 1; 449 | } 450 | return 0; 451 | } 452 | 453 | 454 | /* basicf - basic FADD/FSUB/FMUL/FDIV 455 | * 456 | * The guide says that overflow and underflow are detected on the 457 | * exponent. The mantissa is maintained, and the exponent is offset 458 | * by 128. So... that is what we do. Note that frexp() and ldexp() 459 | * should be implemented via bit operations, not arithmetic. 460 | */ 461 | static void basicf(struct am_context *ctx) { 462 | unsigned char *ap, *bp; 463 | float a, b, r; 464 | double m; 465 | int e; 466 | 467 | ap = stpos(-4); 468 | am_fp(ap, ctx->fptmp); 469 | fp_na(ctx->fptmp, &a); 470 | 471 | bp = stpos(-8); 472 | am_fp(bp, ctx->fptmp); 473 | fp_na(ctx->fptmp, &b); 474 | 475 | switch (ctx->op_latch & AM_OP) { 476 | case AM_FADD: 477 | r = a + b; 478 | break; 479 | case AM_FSUB: 480 | r = b - a; 481 | break; 482 | case AM_FMUL: 483 | r = a * b; 484 | break; 485 | case AM_FDIV: 486 | if (a == 0.0) { 487 | r = b; 488 | ctx->status |= AM_ERR_DIV0; 489 | } else 490 | r = b / a; 491 | break; 492 | } 493 | 494 | /* We do not use fov() because we want to bias exponent by 128 495 | * on OVF/UND per the guide. 496 | */ 497 | m = frexp(r, &e); 498 | if (e > 63) { 499 | ctx->status |= AM_ERR_OVF; 500 | e -= 128; 501 | r = ldexp(m, e); 502 | } else if (e < -64) { 503 | ctx->status |= AM_ERR_UND; 504 | e += 128; 505 | r = ldexp(m, e); 506 | } 507 | na_fp(&r, ctx->fptmp); 508 | fp_am(ctx->fptmp, bp); 509 | dec_sp(4); 510 | ctx->op_latch = AM_FLOAT; 511 | sz(ctx); 512 | } 513 | 514 | 515 | /* SQRT EXP SIN COS TAN LN LOG etc (functions with single arg) 516 | * 517 | * Note that we use the -lm math library with GCC, and the -LF library 518 | * with HI-TECH C. This means we are limited to only using functions 519 | * that are in both. This explains the strange shenanigans with double 520 | * here. 521 | */ 522 | static void ffunc(struct am_context *ctx) { 523 | unsigned char *ap; 524 | float a; 525 | double x; 526 | 527 | ap = stpos(-4); 528 | am_fp(ap, ctx->fptmp); 529 | fp_na(ctx->fptmp, &a); 530 | 531 | x = a; 532 | switch (ctx->op_latch & AM_OP) { 533 | case AM_SQRT: 534 | if (a < 0.0) { 535 | ctx->status |= AM_ERR_NEG; 536 | goto err; 537 | } 538 | x = sqrt(x); 539 | break; 540 | case AM_EXP: 541 | /* -1.0 x 2^5 .. 1.0 x 2^5 */ 542 | if ((a < -32.0) || (a > 32.0)) { 543 | ctx->status |= AM_ERR_ARG; 544 | goto err; 545 | } 546 | x = exp(x); 547 | break; 548 | case AM_SIN: 549 | x = sin(x); 550 | break; 551 | case AM_COS: 552 | x = cos(x); 553 | break; 554 | case AM_TAN: 555 | /* less than 2^-12 : return A as tan(A) */ 556 | if (a >= (1.0 / 4096.0)) 557 | x = tan(x); 558 | break; 559 | case AM_LN: 560 | if (a < 0.0) { 561 | ctx->status |= AM_ERR_NEG; 562 | goto err; 563 | } 564 | x = log(x); 565 | break; 566 | case AM_LOG: 567 | if (a < 0.0) { 568 | ctx->status |= AM_ERR_NEG; 569 | goto err; 570 | } 571 | x = log10(x); 572 | break; 573 | case AM_ASIN: 574 | if ((a < -1.0) || (a > 1.0)) { 575 | ctx->status |= AM_ERR_ARG; 576 | goto err; 577 | } 578 | x = asin(x); 579 | break; 580 | case AM_ACOS: 581 | if ((a < -1.0) || (a > 1.0)) { 582 | ctx->status |= AM_ERR_ARG; 583 | goto err; 584 | } 585 | x = acos(x); 586 | break; 587 | case AM_ATAN: 588 | x = atan(x); 589 | break; 590 | } 591 | if (fov(ctx, x)) 592 | goto err; 593 | a = x; 594 | na_fp(&a, ctx->fptmp); 595 | fp_am(ctx->fptmp, ap); 596 | err: 597 | ctx->op_latch = AM_FLOAT; 598 | sz(ctx); 599 | } 600 | 601 | 602 | /* PWR 603 | * 604 | * B^A = EXP( A * LN(B) ) 605 | */ 606 | static void pwr(struct am_context *ctx) { 607 | /* B^A = EXP( A * LN(B) ) */ 608 | unsigned char *ap, *bp; 609 | float a, b; 610 | double x; 611 | 612 | /* A */ 613 | ap = stpos(-4); 614 | am_fp(ap, ctx->fptmp); 615 | fp_na(ctx->fptmp, &a); 616 | 617 | /* B */ 618 | bp = stpos(-8); 619 | am_fp(bp, ctx->fptmp); 620 | fp_na(ctx->fptmp, &b); 621 | 622 | /* LN(B) */ 623 | if (b < 0.0) { 624 | ctx->status |= AM_ERR_NEG; 625 | goto err; 626 | } 627 | x = b; 628 | x = log(x); 629 | 630 | /* A * LN(B) */ 631 | x = (double)a * x; 632 | 633 | /* EXP( A * LN(B) ) */ 634 | if ((x < -32.0) || (x > 32.0)) { 635 | ctx->status |= AM_ERR_ARG; 636 | goto err; 637 | } 638 | x = exp(x); 639 | 640 | if (fov(ctx, x)) 641 | goto err; 642 | 643 | /* replace B with result */ 644 | b = x; 645 | na_fp(&b, ctx->fptmp); 646 | fp_am(ctx->fptmp, bp); 647 | 648 | /* roll stack */ 649 | dec_sp(4); 650 | err: 651 | sz(ctx); 652 | } 653 | 654 | 655 | /* Issue am9511 command. Does not return until command 656 | * is complete. 657 | */ 658 | void am_command(void *amp, unsigned char op) { 659 | struct am_context *ctx = (struct am_context *)amp; 660 | 661 | ctx->op_latch = op; 662 | 663 | #ifndef NDEBUG 664 | ctx->last_latch = op; 665 | #endif 666 | 667 | ctx->status = AM_BUSY; 668 | 669 | switch (ctx->op_latch & AM_OP) { 670 | 671 | case AM_NOP: /* no operation */ 672 | ctx->status = 0; 673 | break; 674 | 675 | case AM_PUPI: /* push pi */ 676 | pupi(ctx); 677 | break; 678 | 679 | case AM_CHS: /* change sign */ 680 | chs(ctx); 681 | break; 682 | 683 | case AM_CHSF: /* float change sign */ 684 | chsf(ctx); /* per Wayne Hortensius */ 685 | break; 686 | 687 | case AM_POP: /* pop */ 688 | pop(ctx); 689 | break; 690 | 691 | case AM_PTO: /* push tos (copy) */ 692 | pto(ctx); 693 | break; 694 | 695 | case AM_XCH: /* exchange tos and nos */ 696 | xch(ctx); 697 | break; 698 | 699 | case AM_FLTD: /* 32 bit to float */ 700 | fltd(ctx); 701 | break; 702 | 703 | case AM_FLTS: /* 16 bit to float */ 704 | flts(ctx); 705 | break; 706 | 707 | case AM_FIXD: /* float to 32 bit */ 708 | fixd(ctx); 709 | break; 710 | 711 | case AM_FIXS: /* float to 16 bit */ 712 | fixs(ctx); 713 | break; 714 | 715 | case AM_ADD: /* add */ 716 | add(ctx); 717 | break; 718 | 719 | case AM_SUB: /* subtract nos-tos */ 720 | sub(ctx); 721 | break; 722 | 723 | case AM_MUL: /* multiply, lower half */ 724 | mul(ctx); 725 | break; 726 | 727 | case AM_MUU: /* multiply, upper half */ 728 | muu(ctx); 729 | break; 730 | 731 | case AM_DIV: /* divide nos/tos */ 732 | divi(ctx); 733 | break; 734 | 735 | case AM_FADD: /* floating add */ 736 | case AM_FSUB: /* floating subtract */ 737 | case AM_FMUL: /* floating multiply */ 738 | case AM_FDIV: /* floating divide */ 739 | basicf(ctx); 740 | break; 741 | 742 | case AM_SQRT: /* square root */ 743 | case AM_EXP: /* exponential (e^x) */ 744 | case AM_SIN: /* sine */ 745 | case AM_COS: /* cosine */ 746 | case AM_TAN: /* tangent */ 747 | case AM_LOG: /* common logarithm (base 10) */ 748 | case AM_LN: /* natural logarthm (base e) */ 749 | case AM_ASIN: /* inverse sine */ 750 | case AM_ACOS: /* inverse cosine */ 751 | case AM_ATAN: /* inverse tangent */ 752 | ffunc(ctx); 753 | break; 754 | 755 | case AM_PWR: /* power nos^tos */ 756 | pwr(ctx); 757 | break; 758 | 759 | default: 760 | break; 761 | } 762 | 763 | ctx->status &= ~AM_BUSY; 764 | } 765 | 766 | 767 | /* Reset the am9511 emulator 768 | */ 769 | void am_reset(void *amp) { 770 | struct am_context *ctx = (struct am_context *)amp; 771 | int i; 772 | 773 | ctx->sp = 0; 774 | ctx->status = 0; 775 | ctx->op_latch = 0; 776 | ctx->last_latch = 0; 777 | for (i = 0; i < 16; ++i) 778 | ctx->stack[i] = 0; 779 | } 780 | 781 | 782 | /* Create chip. 783 | */ 784 | void *am_create(int status, int data) { 785 | struct am_context *p; 786 | void *fpp; 787 | fpp = malloc(fp_size()); 788 | if (fpp == NULL) 789 | return NULL; 790 | p = malloc(sizeof (struct am_context)); 791 | if (p == NULL) { 792 | free(fpp); 793 | return NULL; 794 | } 795 | p->fptmp = fpp; 796 | am_reset(p); 797 | return (void *)p; 798 | } 799 | 800 | 801 | /* Dump stack A..H or A..D, format depends on arg (AM_SINGLE, 802 | * AM_DOUBLE, AM_FLOAT). Dump status and last op_latch. 803 | */ 804 | void am_dump(void *amp, unsigned char op) { 805 | struct am_context *ctx = (struct am_context *)amp; 806 | int i; 807 | int16 n; 808 | int32 nl; 809 | float x; 810 | unsigned char t = ctx->status; 811 | int b; 812 | static char *opnames[] = { 813 | "NOP", "SQRT", "SIN", "COS", 814 | "TAN", "ASIN", "ACOS", "ATAN", 815 | "LOG", "LN", "EXP", "PWR", 816 | "ADD", "SUB", "MUL", "DIV", 817 | "FADD", "FSUB", "FMUL", "FDIV", 818 | "CHS", "CHSF", "MUU", "PTO", 819 | "POP", "XCH", "PUPI", "", 820 | "FLTD", "FLTS", "FIXD", "FIXS" 821 | }; 822 | 823 | printf("AM9511 STATUS: %02x ", ctx->status); 824 | if (t & AM_BUSY) printf("BUSY "); 825 | if (t & AM_SIGN) printf("SIGN "); 826 | if (t & AM_ZERO) printf("ZERO "); 827 | if (t & AM_CARRY) printf("CARRY "); 828 | printf("ERROR: "); 829 | t &= AM_ERR_MASK; 830 | if (t == AM_ERR_NONE) printf("NONE"); 831 | if (t & AM_ERR_DIV0) printf("DIV0"); 832 | if (t & AM_ERR_NEG) printf("NEG"); 833 | if (t & AM_ERR_ARG) printf("ARG"); 834 | if (t & AM_ERR_ARG) printf("ARG"); 835 | if (t & AM_ERR_UND) printf("UND"); 836 | if (t & AM_ERR_OVF) printf("OVF"); 837 | printf("\n"); 838 | t = ctx->last_latch; 839 | printf("LAST COMMAND: "); 840 | if (t & AM_SR) printf("SR "); 841 | if ((t & AM_SINGLE) == AM_SINGLE) printf("SINGLE "); 842 | else if (t & AM_FIXED) printf("DOUBLE "); 843 | else printf("FLOAT "); 844 | printf("%s\n", opnames[t & AM_OP]); 845 | t = ctx->op_latch; 846 | ctx->op_latch = op; 847 | printf("AM9511 STACK "); 848 | if (IS_SINGLE) { 849 | printf("(SINGLE)\n"); 850 | for (i = 0; i < 8; ++i) { 851 | n = *stpos(-(i * 2) - 1); 852 | n = (n << 8) | *stpos(-(i * 2) - 2); 853 | printf("%c: %02x %02x %d\n", 'A' + i, 854 | *stpos(-(i * 2) - 1), 855 | *stpos(-(i * 2) - 2), 856 | n); 857 | } 858 | } else { 859 | if (IS_FIXED) 860 | printf("(DOUBLE)\n"); 861 | else 862 | printf("(FLOAT)\n"); 863 | for (i = 0; i < 4; ++i) { 864 | printf("%c: %02x %02x %02x %02x ", 'A' + i, 865 | *stpos(-(i * 4) - 1), 866 | *stpos(-(i * 4) - 2), 867 | *stpos(-(i * 4) - 3), 868 | *stpos(-(i * 4) - 4)); 869 | if (IS_FIXED) { 870 | #if 0 871 | /* Borked -- HI-TECH C bug 872 | * 873 | * We have seen this before -- use a "fix" (work-around) 874 | */ 875 | nl = *stpos(-(i * 4) - 1); 876 | nl = (nl << 8) | *stpos(-(i * 4) - 2); 877 | nl = (nl << 8) | *stpos(-(i * 4) - 3); 878 | nl = (nl << 8) | *stpos(-(i * 4) - 4); 879 | #else 880 | /* We use the following instead, which seems to work 881 | */ 882 | b = *stpos(-(i * 4) - 1); 883 | nl = b; 884 | 885 | nl = nl << 8; 886 | b = *stpos(-(i * 4) - 2); 887 | nl = nl | b; 888 | 889 | nl = nl << 8; 890 | b = *stpos(-(i * 4) - 3); 891 | nl = nl | b; 892 | 893 | nl = nl << 8; 894 | b = *stpos(-(i * 4) - 4); 895 | nl = nl | b; 896 | #endif 897 | printf("%ld\n", (long)nl); 898 | } else { 899 | am_fp(stpos(-(i * 4) - 4), ctx->fptmp); 900 | fp_na(ctx->fptmp, &x); 901 | printf("%g\n", x); 902 | } 903 | } 904 | } 905 | ctx->op_latch = t; 906 | } 907 | -------------------------------------------------------------------------------- /am9511.h: -------------------------------------------------------------------------------- 1 | /* am9511.h 2 | */ 3 | 4 | #ifndef _AM9511_H 5 | #define _AM9511_H 6 | 7 | /* Smallest and largest numbers in the AM9511 floating point 8 | * format. 0.5x2^-64 to 0.99999..x2^63. 9 | * 10 | * As a note: these values can be exactly computed: 11 | * 12 | * unsigned char am_small[4], am_big[4]; 13 | * fp_put(0, -64, 0x80, 0x0000); will generate AM_SMALL 14 | * fp_am(am_small); 15 | * fp_put(0, 63, 0xff, 0xffff); will generate AM_BIG 16 | * fp_am(am_big); 17 | */ 18 | #define AM_SMALL 2.71051e-20 19 | #define AM_BIG 9.22337e+18 20 | #define AM_PI 3.141592 21 | 22 | #define AM_SR 0x80 /* service request on completion */ 23 | #define AM_SINGLE 0x60 /* 16 bit integer */ 24 | #define AM_DOUBLE 0x20 /* 32 bit integer */ 25 | #define AM_FIXED 0x20 /* fixed point */ 26 | #define AM_FLOAT 0x00 /* 32 bit float */ 27 | 28 | #define AM_NOP 0x00 /* no operation */ 29 | #define AM_SQRT 0x01 /* square root */ 30 | #define AM_SIN 0x02 /* sine */ 31 | #define AM_COS 0x03 /* cosine */ 32 | #define AM_TAN 0x04 /* tangent */ 33 | #define AM_ASIN 0x05 /* inverse sine */ 34 | #define AM_ACOS 0x06 /* inverse cosine */ 35 | #define AM_ATAN 0x07 /* inverse tangent */ 36 | #define AM_LOG 0x08 /* common logarithm (base 10) */ 37 | #define AM_LN 0x09 /* natural logairth (base e) */ 38 | #define AM_EXP 0x0a /* exponential (e^x) */ 39 | #define AM_PWR 0x0b /* power nos^tos */ 40 | #define AM_ADD 0x0c /* add */ 41 | #define AM_SUB 0x0d /* subtract nos-tos */ 42 | #define AM_MUL 0x0e /* multiply, lower half */ 43 | #define AM_DIV 0x0f /* divide nos/tos */ 44 | #define AM_FADD 0x10 /* floating add */ 45 | #define AM_FSUB 0x11 /* floating subtract */ 46 | #define AM_FMUL 0x12 /* floating multiply */ 47 | #define AM_FDIV 0x13 /* floating divide */ 48 | #define AM_CHS 0x14 /* change sign */ 49 | #define AM_CHSF 0x15 /* floating change sign */ 50 | #define AM_MUU 0x16 /* multiply, upper half */ 51 | #define AM_PTO 0x17 /* push tos to nos (copy) */ 52 | #define AM_POP 0x18 /* pop */ 53 | #define AM_XCH 0x19 /* exchange tos and nos */ 54 | #define AM_PUPI 0x1a /* push pi */ 55 | /* 0x1b */ 56 | #define AM_FLTD 0x1c /* 32 bit to float */ 57 | #define AM_FLTS 0x1d /* 16 bit to float */ 58 | #define AM_FIXD 0x1e /* float to 32 bit */ 59 | #define AM_FIXS 0x1f /* float to 16 bit */ 60 | 61 | #define AM_BUSY 0x80 /* chip is busy */ 62 | #define AM_SIGN 0x40 /* tos negative */ 63 | #define AM_ZERO 0x20 /* tos zero */ 64 | #define AM_ERR_MASK 0x1E /* mask for errors */ 65 | #define AM_CARRY 0x01 /* carry/borrow from most significant bit */ 66 | 67 | #define AM_ERR_NONE 0x00 /* no error */ 68 | #define AM_ERR_DIV0 0x10 /* divide by zero */ 69 | #define AM_ERR_NEG 0x08 /* sqrt, log of negative */ 70 | #define AM_ERR_ARG 0x18 /* arg of asin, cos, e^x too large */ 71 | #define AM_ERR_UND 0x04 /* underflow */ 72 | #define AM_ERR_OVF 0x02 /* overflow */ 73 | 74 | void *am_create(int status, int data); 75 | void am_push(void *, unsigned char); 76 | unsigned char am_pop(void *); 77 | unsigned char am_status(void *); 78 | void am_command(void *, unsigned char); 79 | void am_reset(void *); 80 | 81 | #ifdef NDEBUG 82 | #define am_dump(x) 83 | #else 84 | void am_dump(void *, unsigned char); 85 | #endif 86 | 87 | #endif 88 | 89 | -------------------------------------------------------------------------------- /ansi.h: -------------------------------------------------------------------------------- 1 | /* ansi.h 2 | * 3 | * Make HI-TECH C a bit more ANSI C 89 compatible. Note that the 4 | * last revision of HI-TECH C 3.09 was released in 1987 -- the 5 | * ANSI C 89 standard was submitted in 1988. So, this is not a 6 | * fault of HI-TECH C. 7 | */ 8 | 9 | 10 | #ifndef ANSI_H 11 | #define ANSI_H 12 | 13 | #ifdef z80 14 | 15 | /* Not reserved in HI-TECH C 3.09 16 | */ 17 | #define signed 18 | #define const 19 | #define volatile 20 | 21 | #endif 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /build: -------------------------------------------------------------------------------- 1 | # build 2 | # 3 | # Build am9511 emulator 4 | # 5 | # For the hardware version, we do not need ova (integer overflow 6 | # math) or the am9511 front end or floating emulation. This is 7 | # all done by the am9511 chip, through hw9511. We *do* need floatcnv 8 | # to be able to make sense of the results of the chip. 9 | # 10 | # For the software version (test.com, vs the hardware testhw.com), 11 | # we use ova for integer math, am9511 for command interpretation. 12 | # 13 | # The main routine test.c is used for both hardware and emulation. 14 | # This is the test driver for the emulation and the chip. 15 | # 16 | # Choose the appropriate build sequence. gcc or zxc. gcc is the 17 | # actual target, because this code is to be incorporated into 8080/ 18 | # Z80 emulators to provide AM9511 chip emulation. 19 | # 20 | # NOTE: Our initial target is AM9511 emulation and *not* AM9512 emulation. 21 | # AM9511 offers more functions, and 16 and 32 bit integer operations. 22 | # This can more easily enhance typical programs running on the 23 | # 8080/Z80 platform. 16 bit add and subtract are not interesting -- 24 | # multiply and divide are. As are the 32 bit (double) operations. 25 | # 26 | # WHY: The AM9511 can deliver a floating point divide in 165.9 27 | # 2Mhz 8080 cycles, or 41 register instructions. Compare to LLLBASIC 28 | # at 13079.6 cycles, this is 78.8 times faster. 29 | # 30 | # We are going to incorporate am9511 into RunCPM for testing. 31 | # We will see if we can get a floating point divide in the 32 | # space of 41 instructions. When we get this, we are pretty 33 | # much complete, and can submit for inclusion into z80pack 34 | # and altair-duino. 35 | 36 | # set -v to display without expansion 37 | set -x 38 | 39 | if true; then 40 | 41 | # Build for gcc (emulation) 42 | # 43 | # -I. needed because #include has to find ansi.h 44 | # 45 | # Compile hw9511.c to validate compile on gcc 46 | # 47 | echo building test 48 | gcc -O3 -I. -Wall -c hw9511.c 49 | gcc -O3 -I. -Wall -o test test.c getopt.c am9511.c floatcnv.c ova.c -lm 50 | gcc -O3 -I. -Wall -DTEST1 -DTEST2 -DTEST3 -DTEST4 -o test14 \ 51 | test.c getopt.c am9511.c floatcnv.c ova.c -lm 52 | gcc -O3 -I. -Wall -DTEST5 -DTEST6 -DTEST7 -DTEST8 -o test58 \ 53 | test.c getopt.c am9511.c floatcnv.c ova.c -lm 54 | 55 | fi 56 | 57 | if true; then 58 | 59 | # Build for z80 using zxcc (validation) 60 | # 61 | # We optimize everything, to get smallest/best code. Need -LF 62 | # on link for floating point. 63 | # 64 | # Do NOT optimize hw9511.c, as that has inline assembler in 65 | # it. Attempting to run OPTIM on that can result in very strange 66 | # things -- like the compiler simply hanging up. 67 | # 68 | echo building test.com 69 | zxc -c hw9511.c 70 | zxc -c -o getopt.c 71 | zxc -c -o am9511.c 72 | zxc -c -o floatcnv.c 73 | zxc -c -o ova.c 74 | # 75 | zxc cnvtest.c floatcnv.obj -LF 76 | # 77 | rm test.obj 78 | zxc -c -o -DTEST1 -DTEST2 -DTEST3 -DTEST4 test.c 79 | zxc test.obj getopt.obj am9511.obj floatcnv.obj ova.obj -LF 80 | cp test.com test14.com 81 | zxc test.obj getopt.obj hw9511.obj floatcnv.obj -LF 82 | cp test.com testhw14.com 83 | # 84 | rm test.obj 85 | zxc -c -o -DTEST5 -DTEST6 -DTEST7 -DTEST8 test.c 86 | zxc test.obj getopt.obj am9511.obj floatcnv.obj ova.obj -LF 87 | cp test.com test58.com 88 | zxc test.obj getopt.obj hw9511.obj floatcnv.obj -LF 89 | cp test.com testhw58.com 90 | # 91 | rm test.obj 92 | zxc -c -o test.c 93 | zxc test.obj getopt.obj hw9511.obj floatcnv.obj -LF 94 | cp test.com testhw.com 95 | zxc test.obj getopt.obj am9511.obj floatcnv.obj ova.obj -LF 96 | fi 97 | -------------------------------------------------------------------------------- /cnvtest.c: -------------------------------------------------------------------------------- 1 | /* cnvtest.c 2 | */ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "floatcnv.h" 11 | 12 | 13 | /* Dump FP, using fp_get() to take it apart. 14 | */ 15 | void fp_dump(void *fpp) { 16 | unsigned char sign; 17 | int exponent; 18 | unsigned char mantissa_h; 19 | unsigned int mantissa_l; 20 | 21 | fp_get(fpp, &sign, &exponent, &mantissa_h, &mantissa_l); 22 | printf(" %c0x%02x%04x*2^%d\n", sign ? '-' : '+', 23 | mantissa_h, 24 | mantissa_l, 25 | exponent); 26 | } 27 | 28 | 29 | /* We are record known-good 4 byte sequences in our different 30 | * floating point formats, to convert and print for validation. 31 | * 32 | * note msb comes first in am9511 when reading stack. 33 | */ 34 | unsigned char am9511_pi[] = { /* am9511 pi */ 35 | 0xda, 0x0f, 0xc9, 0x02 36 | }; 37 | unsigned char am9511_5[] = { /* am9511 5.0 */ 38 | 0x00, 0x00, 0xa0, 0x03 39 | }; 40 | unsigned char am9511_p1[] = { /* am9511 0.1 */ 41 | 0xcd, 0xcc, 0xcc, 0x7d 42 | }; 43 | unsigned char am9511_mp0006[] = { /* am9511 -0.0006 */ 44 | 0x51, 0x49, 0x9d, 0xf6 45 | }; 46 | unsigned char ieee_m5[] = { /* ieee -5.0 */ 47 | 0x00, 0x00, 0xa0, 0xc0 48 | }; 49 | unsigned char ms_m1p5[] = { /* microsoft -1.5 */ 50 | 0x00, 0x00, 0xc0, 0x81 51 | }; 52 | unsigned char hi_p1[] = { /* hitech 0.1 */ 53 | 0xcd, 0xcc, 0xcc, 0x3d 54 | }; 55 | 56 | /* Dump 4 bytes in hex. 57 | */ 58 | void dump4(void *p) { 59 | unsigned char *u = p; 60 | int i; 61 | for (i = 0; i < 4; ++i) 62 | printf("%02x ", u[i]); 63 | printf("\n"); 64 | } 65 | 66 | /* Choose target for floating output. 67 | */ 68 | #ifdef z80 69 | #define fp_target(p) fp_hi(fpp, p) 70 | #else 71 | #define fp_target(p) fp_ie(fpp, p) 72 | #endif 73 | 74 | int main(int ac, char **av) { 75 | float x; 76 | void *fpp; 77 | 78 | fpp = malloc(fp_size()); 79 | if (fpp == NULL) { 80 | fprintf(stderr, "cannot allocate temp\n"); 81 | return 1; 82 | } 83 | am_fp(am9511_pi, fpp); 84 | fp_target(&x); 85 | printf("am9511 pi: %10g\n", x); 86 | fp_dump(fpp); 87 | 88 | am_fp(am9511_5, fpp); 89 | fp_target(&x); 90 | printf("am9511 5: %10g\n", x); 91 | fp_dump(fpp); 92 | 93 | am_fp(am9511_p1, fpp); /* exp=0x40 */ 94 | fp_target(&x); 95 | printf("am9511 0.1: %10g\n", x); 96 | fp_dump(fpp); 97 | 98 | am_fp(am9511_mp0006, fpp); /* exp = 0xc0 */ 99 | fp_target(&x); 100 | printf("am9511 -0.0006: %10g\n", x); 101 | fp_dump(fpp); 102 | 103 | ie_fp(ieee_m5, fpp); 104 | fp_target(&x); 105 | printf("ieee -5: %10g\n", x); 106 | fp_dump(fpp); 107 | 108 | hi_fp(hi_p1, fpp); 109 | fp_target(&x); 110 | printf("hi 0.1: %10g\n", x); 111 | fp_dump(fpp); 112 | 113 | ms_fp(ms_m1p5, fpp); 114 | fp_target(&x); 115 | printf("ms -1.5: %10g\n", x); 116 | fp_dump(fpp); 117 | 118 | return 0; 119 | } 120 | -------------------------------------------------------------------------------- /cnvtest.com: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ratboy666/am9511/ab8ed57f1a3eab09917f42f72f0ab70a74330501/cnvtest.com -------------------------------------------------------------------------------- /floatcnv.c: -------------------------------------------------------------------------------- 1 | /* floatcnv.c 2 | * 3 | * Floating point conversions. 4 | * 5 | * We are able to convert data to and from different floating point 6 | * formats. 7 | * 8 | * The conversion code uses an intermediate format "fp". "fp" is not a 9 | * superset of the formats, because it does not implement IEEE NAN or 10 | * denormalized numbers. 11 | * 12 | * Formats: 13 | * 14 | * Format Notes 15 | * 16 | * ms Microsoft 32 bit (mbasic/f80) 17 | * am AM9511A 32 bit 18 | * hi Hitech C 32 bit 19 | * ie IEEE 32 bit (Turbo Modula 2 REAL, gcc float) 20 | * 21 | * Compile and test with GCC, TCC and HI-TECH C. Floating point conversion 22 | * code is of use on the z80 platform to prepare input for the AM9511. 23 | * 24 | * Function naming conventions: 25 | * 26 | * As a matter of interest, since the conversion code is targetted to 27 | * z80 native, possibly using Microsoft REL format, identifiers are kept 28 | * to 6 characters significance. Since Hi-Tech C prepends an '_' to names, 29 | * this only gives us 5 characters. Therefore, Microsoft 32 to internal is 30 | * ms_fp(), and internal to AM9511 is fp_am(). 31 | */ 32 | 33 | 34 | #include 35 | 36 | #include "floatcnv.h" 37 | #include "types.h" 38 | 39 | 40 | /* I0..I3 support endian systems other than little endian. Unfortunately, 41 | * I do not have access to big endian systems anymore. 42 | */ 43 | #define I0 0 /* least significant byte */ 44 | #define I1 1 45 | #define I2 2 46 | #define I3 3 /* most significant byte */ 47 | 48 | 49 | /* Internal floating point representation, separate sign, normalized 24 bit 50 | * mantissa with explicit high "1" bit. 51 | * If mantissa high bit == 0, value is 0.0. 52 | * Exponent is not biased. 53 | * Separate sign (1 = negative, 0 = positive) 54 | */ 55 | struct fp { 56 | uint16 mantissa_l; 57 | int16 exponent; 58 | uint8 sign; 59 | uint8 mantissa_h; 60 | }; 61 | 62 | 63 | /* memset() is broken with HI-TECH C. 64 | * The memset() issue prompts us to write and use clear() instead. We avoid 65 | * ong (32 bit) values to avoid linking in library routines on the z80. We 66 | * are limiting ourselves to 8 and 16 bit operations. "register" declarations 67 | * will be added... by examining z80 assembler output... 68 | */ 69 | static void clear(void *p, int n) { 70 | unsigned char *cp = (unsigned char *)p; 71 | while (n--) 72 | *cp++ = 0; 73 | } 74 | 75 | 76 | /* Convert IEEE to FP format. 77 | * 78 | * These routines are written for clarity and portability, not speed. 79 | * Assumptions are that "unsigned char" is 8 bit (0..255). Incoming 80 | * pointer p is to point to a IEEE format 32 bit float. If this is in 81 | * "little-endian" order, I0=0, I1=1, I2=2 and I3=3 can be used. If 82 | * this is in "big endian", try I0=3, I1=2, I2=1 and I3=0. We are 83 | * assuming that the IEEE float occupies 4 bytes of contiguous memory, 84 | * starting with pointer p. 85 | */ 86 | int ie_fp(void *p, void *fpp) { 87 | /* This assignment does not need a cast in C, but gives warning 88 | * if the cast is not used in C++ 89 | */ 90 | unsigned char *ieee = (unsigned char *)p; 91 | struct fp *fptmp = (struct fp *)fpp; 92 | int16 e; 93 | uint16 m_l; 94 | uint8 m_h, s; 95 | 96 | /* Clear destination 97 | */ 98 | clear(fptmp, sizeof(struct fp)); 99 | 100 | /* Extract exponent. 7 bits from I2, bit from I3. 101 | */ 102 | e = ((ieee[I3] & 0x7f) << 1) | 103 | ((ieee[I2] & 0x80) != 0); 104 | 105 | /* Extract sign. 1 if negative, 0 if positive. 106 | */ 107 | s = (ieee[I3] & 0x80) != 0; 108 | 109 | /* If exponent == 0, number is zero. 110 | */ 111 | if (e == 0) 112 | return FP_OK; 113 | 114 | /* If exponent == 0xff, number is NAN, or one of the other specials. 115 | */ 116 | if (e == 0xff) 117 | return FP_ERR; 118 | 119 | /* Unbias the exponent. 120 | */ 121 | e -= 127; 122 | 123 | /* Get 23 bits of mantissa 124 | */ 125 | m_h = ieee[I2] & 0x7f; 126 | m_l = ieee[I1]; 127 | m_l = (m_l << 8) | ieee[I0]; 128 | 129 | /* Add leading 1 bit, which was implied. 130 | */ 131 | m_h |= 0x80; 132 | 133 | /* Save into fp. 134 | */ 135 | fptmp->exponent = e; 136 | fptmp->sign = s; 137 | fptmp->mantissa_h = m_h; 138 | fptmp->mantissa_l = m_l; 139 | 140 | return FP_OK; 141 | } 142 | 143 | 144 | /* Convert FP to IEEE format. 145 | */ 146 | int fp_ie(void *fpp, void *p) { 147 | unsigned char *ieee = (unsigned char *)p; 148 | struct fp *fptmp = (struct fp *)fpp; 149 | int16 e; 150 | uint16 m_l; 151 | uint8 m_h, s; 152 | 153 | /* Get from fp. 154 | */ 155 | e = fptmp->exponent; 156 | m_h = fptmp->mantissa_h; 157 | m_l = fptmp->mantissa_l; 158 | s = fptmp->sign; 159 | 160 | /* Clear destination. 161 | */ 162 | clear(ieee, 4); 163 | 164 | /* If mantissa bit 23 is 0, result is 0.0. 165 | */ 166 | if ((m_h & 0x80) == 0) 167 | return FP_OK; 168 | 169 | /* Strip leading 1 bit from mantissa 170 | */ 171 | m_h &= 0x7f; 172 | 173 | /* Range check exponent. 174 | */ 175 | if (e < -126) 176 | return FP_ERR; 177 | if (e > 127) 178 | return FP_ERR; 179 | 180 | /* Bias exponent. 181 | */ 182 | e += 127; 183 | 184 | /* Low bit of exponent goes into mantissa 185 | */ 186 | if (e & 1) 187 | m_h |= 0x80; 188 | e = (e >> 1) & 0x7f; 189 | 190 | /* Sign goes to into exponent 191 | */ 192 | if (s) 193 | e |= 0x80; 194 | 195 | /* Put results into memory 196 | */ 197 | ieee[I0] = m_l & 0xff; 198 | ieee[I1] = (m_l >> 8) & 0xff; 199 | ieee[I2] = m_h & 0xff; 200 | ieee[I3] = e & 0xff; 201 | 202 | return FP_OK; 203 | } 204 | 205 | 206 | /* Convert HITECH to FP format. 207 | */ 208 | int hi_fp(void *p, void *fpp) { 209 | unsigned char *hitech = (unsigned char *)p; 210 | struct fp *fptmp = (struct fp *)fpp; 211 | int16 e; 212 | uint16 m_l; 213 | uint8 m_h, s; 214 | 215 | /* Clear destination 216 | */ 217 | clear(fptmp, sizeof(struct fp)); 218 | 219 | /* If bit 23 is zero, must be zero (or not normalized). 220 | */ 221 | if ((hitech[I2] & 0x80) == 0) 222 | return FP_OK; 223 | 224 | /* Gather up sign, and exponent. Remove sign 225 | * bit from exponent. 226 | */ 227 | s = (hitech[I3] & 0x80) != 0; 228 | e = hitech[I3] & 0x7f; 229 | 230 | /* Gather mantissa. Note that leading '1' bit is there. 231 | */ 232 | m_h = hitech[I2]; 233 | m_l = hitech[I1]; 234 | m_l = (m_l << 8) | hitech[I0]; 235 | 236 | /* Unbias the exponent. 237 | */ 238 | e -= 65; 239 | 240 | /* Put into fp. 241 | */ 242 | fptmp->sign = s; 243 | fptmp->exponent = e; 244 | fptmp->mantissa_l = m_l; 245 | fptmp->mantissa_h = m_h; 246 | 247 | return FP_OK; 248 | } 249 | 250 | 251 | /* Convert FP to HITECH format. 252 | */ 253 | int fp_hi(void *fpp, void *p) { 254 | unsigned char *hitech = (unsigned char *)p; 255 | struct fp *fptmp = (struct fp *)fpp; 256 | int16 e; 257 | uint16 m_l; 258 | uint8 m_h, s; 259 | 260 | /* Get from fp. 261 | */ 262 | e = fptmp->exponent; 263 | m_l = fptmp->mantissa_l; 264 | m_h = fptmp->mantissa_h; 265 | s = fptmp->sign; 266 | 267 | /* Clear destination. 268 | */ 269 | clear(hitech, 4); 270 | 271 | /* If mantissa bit 23 is 0, result is 0.0. 272 | */ 273 | if ((m_h & 0x80) == 0) 274 | return FP_OK; 275 | 276 | /* Range check the exponent. 277 | */ 278 | if (e < -65) 279 | return FP_ERR; 280 | if (e > 63) 281 | return FP_ERR; 282 | 283 | /* Bias the exponent. 284 | */ 285 | e += 65; 286 | e &= 0x7f; 287 | 288 | /* Add in sign bit to exponent. 289 | */ 290 | if (s) 291 | e |= 0x80; 292 | 293 | /* Build up output. 294 | */ 295 | hitech[I3] = e & 0xff; 296 | hitech[I2] = m_h & 0xff; 297 | hitech[I1] = (m_l >> 8) & 0xff; 298 | hitech[I0] = m_l & 0xff; 299 | 300 | return FP_OK; 301 | } 302 | 303 | 304 | /* Convert MS to FP format. 305 | */ 306 | int ms_fp(void *p, void *fpp) { 307 | unsigned char *ms = (unsigned char *)p; 308 | struct fp *fptmp = (struct fp *)fpp; 309 | int16 e; 310 | uint16 m_l; 311 | uint8 m_h, s; 312 | 313 | /* Clear destination 314 | */ 315 | clear(fptmp, sizeof(struct fp)); 316 | 317 | /* If exponent is zero, must be zero. 318 | */ 319 | if (ms[I3] == 0) 320 | return FP_OK; 321 | 322 | /* Gather up sign, and exponent. 323 | */ 324 | s = (ms[I2] & 0x80) != 0; 325 | e = ms[I3]; 326 | 327 | /* Gather mantissa. Replace sign bit with leading '1'. 328 | */ 329 | m_h = ms[I2] | 0x80; 330 | m_l = ms[I1]; 331 | m_l = (m_l << 8) | ms[I0]; 332 | 333 | /* Unbias the exponent. 334 | */ 335 | e -= 129; 336 | 337 | /* Put into fp. 338 | */ 339 | fptmp->sign = s; 340 | fptmp->exponent = e; 341 | fptmp->mantissa_l = m_l; 342 | fptmp->mantissa_h = m_h; 343 | 344 | return FP_OK; 345 | } 346 | 347 | 348 | /* Convert FP to MS format. 349 | */ 350 | int fp_ms(void *fpp, void *p) { 351 | unsigned char *ms = (unsigned char *)p; 352 | struct fp *fptmp = (struct fp *)fpp; 353 | int16 e; 354 | uint16 m_l; 355 | uint8 m_h, s; 356 | 357 | /* Get from fp. 358 | */ 359 | e = fptmp->exponent; 360 | m_l = fptmp->mantissa_l; 361 | m_h = fptmp->mantissa_h; 362 | s = fptmp->sign; 363 | 364 | /* Clear destination. 365 | */ 366 | clear(ms, 4); 367 | 368 | /* If mantissa bit 23 is 0, result is 0.0. 369 | */ 370 | if ((m_h & 0x80) == 0) 371 | return FP_OK; 372 | 373 | /* Range check the exponent. 374 | */ 375 | if (e < -129) 376 | return FP_ERR; 377 | if (e > 126) 378 | return FP_ERR; 379 | 380 | /* Bias the exponent. 381 | */ 382 | e += 129; 383 | 384 | /* Replace leading '1' with sign. 385 | */ 386 | m_l &= 0x7f; 387 | if (s) 388 | m_l |= 0x80; 389 | 390 | /* Build up output. 391 | */ 392 | ms[I3] = e & 0xff; 393 | ms[I2] = m_h & 0xff; 394 | ms[I1] = (m_l >> 8) & 0xff; 395 | ms[I0] = m_l & 0xff; 396 | 397 | return FP_OK; 398 | } 399 | 400 | 401 | /* Convert AM9511 to FP format. 402 | */ 403 | int am_fp(void *p, void *fpp) { 404 | unsigned char *am9511 = (unsigned char *)p; 405 | struct fp *fptmp = (struct fp *)fpp; 406 | int16 e; 407 | uint16 m_l; 408 | uint8 m_h, s; 409 | 410 | /* Clear destination 411 | */ 412 | clear(fptmp, sizeof(struct fp)); 413 | 414 | /* If bit 23 is low, must be zero. AM9511 format is 415 | * normalized, but the leading '1' bit is explicit. 416 | * The exponent byte has the sign of the number, and 417 | * 7 bit 2's complement exponent (2^-64..2^63). This 418 | * is then the most limited 32 bit float format we 419 | * have. 420 | */ 421 | if ((am9511[I2] & 0x80) == 0) 422 | return FP_OK; 423 | 424 | /* Gather up sign, and exponent. 425 | */ 426 | s = (am9511[I3] & 0x80) != 0; 427 | e = am9511[I3]; 428 | 429 | /* Gather mantissa. 430 | */ 431 | m_h = am9511[I2]; 432 | m_l = am9511[I1]; 433 | m_l = (m_l << 8) | am9511[I0]; 434 | 435 | /* Exponent is 7 bit, 2's complement. Convert 436 | * to int. 437 | */ 438 | e = e & 0x7f; 439 | if (e & 0x40) { 440 | e = (e ^ 0x7f) + 1; 441 | e = -e; 442 | } 443 | e -= 1; 444 | 445 | /* Fill in fp. 446 | */ 447 | fptmp->sign = s; 448 | fptmp->exponent = e; 449 | fptmp->mantissa_h = m_h; 450 | fptmp->mantissa_l = m_l; 451 | 452 | return FP_OK; 453 | } 454 | 455 | 456 | int fp_am(void *fpp, void *p) { 457 | unsigned char *am9511 = (unsigned char *)p; 458 | struct fp *fptmp = (struct fp *)fpp; 459 | int16 e; 460 | uint16 m_l; 461 | uint8 m_h, s; 462 | 463 | /* Get from fp. 464 | */ 465 | e = fptmp->exponent; 466 | m_l = fptmp->mantissa_l; 467 | m_h = fptmp->mantissa_h; 468 | s = fptmp->sign; 469 | 470 | /* Clear destination. 471 | */ 472 | clear(am9511, 4); 473 | 474 | /* If mantissa bit 23 is 0, result is 0.0. 475 | */ 476 | if ((m_h & 0x80) == 0) 477 | return FP_OK; 478 | 479 | /* Range check on exponent. 480 | */ 481 | if (e < -64) 482 | return FP_ERR; 483 | if (e > 63) 484 | return FP_ERR; 485 | e += 1; 486 | 487 | /* Exponent to 7 bit (assumes 2's complement machine) 488 | */ 489 | e &= 0x7f; 490 | 491 | /* Merge in sign to exponent. 492 | */ 493 | if (s) 494 | e |= 0x80; 495 | 496 | /* Build up output. 497 | */ 498 | am9511[I3] = e & 0xff; 499 | am9511[I2] = m_h & 0xff; 500 | am9511[I1] = (m_l >> 8) & 0xff; 501 | am9511[I0] = m_l & 0xff; 502 | 503 | return FP_OK; 504 | } 505 | 506 | 507 | /* fp field getter. Note that the type of mantissa_l is wrong a lot of the 508 | * time. int is 32 bit with gcc, not 16 bit. The type of exponent is also 509 | * wrong. Just be careful to respect the limits of the fp struct when 510 | * using fp_get()/fp_put(): 511 | * 512 | * sign is 0 or 1 513 | * exponent is -127..128 514 | * mantissa_h is 128..255 (high bit is '1', mantissa always normalized) 515 | * mantissa_l is 0..65535 516 | * 517 | * if (mantissa_h & 0x80) == 0, value is 0.0. This is because the 518 | * high bit of the mantissa is explicit. This gives us a fast 0 519 | * test. 520 | */ 521 | void fp_get(void *fpp, 522 | unsigned char *sign, 523 | int *exponent, 524 | unsigned char *mantissa_h, 525 | unsigned int *mantissa_l) { 526 | struct fp *fptmp = (struct fp *)fpp; 527 | *sign = fptmp->sign; 528 | *exponent = fptmp->exponent; 529 | *mantissa_h = fptmp->mantissa_h; 530 | *mantissa_l = fptmp->mantissa_l; 531 | } 532 | 533 | /* Put into fp. 534 | */ 535 | void fp_put(void *fpp, 536 | unsigned char sign, 537 | int exponent, 538 | unsigned char mantissa_h, 539 | unsigned int mantissa_l) { 540 | struct fp *fptmp = (struct fp *)fpp; 541 | fptmp->sign = sign; 542 | fptmp->exponent = exponent; 543 | fptmp->mantissa_h = mantissa_h; 544 | fptmp->mantissa_l = mantissa_l; 545 | } 546 | 547 | /* Size of internal fp format. 548 | */ 549 | size_t fp_size(void) { 550 | return sizeof (struct fp); 551 | } 552 | 553 | -------------------------------------------------------------------------------- /floatcnv.h: -------------------------------------------------------------------------------- 1 | /* floatcnv.h 2 | * 3 | * Floating point Conversions 4 | * 5 | * We are able to convert data to and from different floating point 6 | * formats. 7 | * 8 | * The conversion code uses an intermediate format "fp". 9 | * "fp" is not a superset of the formats, because it does not implement 10 | * IEEE NAN or denormalized numbers. 11 | * 12 | * Formats: 13 | * 14 | * Format Notes 15 | * 16 | * ms Microsoft 32 bit (mbasic/f80) 17 | * am AM9511A 32 bit 18 | * hi Hitech C 32 bit 19 | * ie IEEE 32 bit (Turbo Modula 2 REAL, gcc float) 20 | * 21 | * Function naming conventions: 22 | * 23 | * As a matter of interest, since the conversion code is targetted to 24 | * z80 native, possibly using Microsoft REL format, identifiers are kept 25 | * to 6 characters signifance. Since Hi-Tech C prepends an '_' to names, 26 | * this only gives us 5 characters. Therefore, Microsoft 32 to internal is 27 | * ms_fp(), and internal to AM9511 is fp_am(). 28 | */ 29 | 30 | #ifndef FLOATCONV_H 31 | #define FLOATCONV_H 32 | 33 | #define FP_OK 0 /* conversion ok */ 34 | #define FP_ERR 1 /* conversion failed (won't fit in format) */ 35 | 36 | int ie_fp(void *, void *); /* ieee to fp */ 37 | int fp_ie(void *, void *); /* fp to ieee */ 38 | int hi_fp(void *, void *); /* hitech to fp */ 39 | int fp_hi(void *, void *); /* fp to hitech */ 40 | int ms_fp(void *, void *); /* microsoft to fp */ 41 | int fp_ms(void *, void *); /* fp to microsoft */ 42 | int am_fp(void *, void *); /* am9511 to fp */ 43 | int fp_am(void *, void *); /* fp to am9511*/ 44 | void fp_get(void *, /* get fp fields */ 45 | unsigned char *sign, 46 | int *exponent, 47 | unsigned char *mantissa_h, 48 | unsigned int *mantissa_l); 49 | void fp_put(void *, /* set fp fields */ 50 | unsigned char sign, 51 | int exponent, 52 | unsigned char mantissa_h, 53 | unsigned int mantissa_l); 54 | size_t fp_size(void); /* size of struct fp */ 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /getopt.3: -------------------------------------------------------------------------------- 1 | .\" Copyright (c) 1988, 1991 Regents of the University of California. 2 | .\" All rights reserved. 3 | .\" 4 | .\" Redistribution and use in source and binary forms, with or without 5 | .\" modification, are permitted provided that the following conditions 6 | .\" are met: 7 | .\" 1. Redistributions of source code must retain the above copyright 8 | .\" notice, this list of conditions and the following disclaimer. 9 | .\" 2. Redistributions in binary form must reproduce the above copyright 10 | .\" notice, this list of conditions and the following disclaimer in the 11 | .\" documentation and/or other materials provided with the distribution. 12 | .\" 3. All advertising materials mentioning features or use of this software 13 | .\" must display the following acknowledgement: 14 | .\" This product includes software developed by the University of 15 | .\" California, Berkeley and its contributors. 16 | .\" 4. Neither the name of the University nor the names of its contributors 17 | .\" may be used to endorse or promote products derived from this software 18 | .\" without specific prior written permission. 19 | .\" 20 | .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 | .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 | .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 | .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 | .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 | .\" SUCH DAMAGE. 31 | .\" 32 | .\" @(#)getopt.3 6.16 (Berkeley) 4/19/91 33 | .\" 34 | .Dd April 19, 1991 35 | .Dt GETOPT 3 36 | .Os BSD 4.3 37 | .Sh NAME 38 | .Nm getopt 39 | .Nd get option letter from argv 40 | .Sh SYNOPSIS 41 | .Fd #include 42 | .Vt extern char *optarg 43 | .Vt extern int optind 44 | .Vt extern int opterr 45 | .Ft int 46 | .Fn getopt "int argc" "char * const *argv" "const char *optstring" 47 | .Sh DESCRIPTION 48 | The 49 | .Fn getopt 50 | function gets 51 | the next 52 | .Em known 53 | option character from 54 | .Fa argv . 55 | An option character is 56 | .Em known 57 | if it has been specified in the string of accepted option characters, 58 | .Fa optstring . 59 | .Pp 60 | The option string 61 | .Fa optstring 62 | may contain the following characters; letters and 63 | letters followed by a colon to indicate an option argument 64 | is to follow. It does not matter to 65 | .Fn getopt 66 | if a following argument has leading white space. 67 | .Pp 68 | On return from 69 | .Fn getopt , 70 | .Va optarg 71 | points to an option argument, if it is anticipated, 72 | and the variable 73 | .Va optind 74 | contains the index to the next 75 | .Fa argv 76 | argument for a subsequent call 77 | to 78 | .Fn getopt . 79 | .Pp 80 | The variable 81 | .Va opterr 82 | and 83 | .Va optind 84 | are both initialized to 1. 85 | In order to use 86 | .Fn getopt 87 | to evaluate multiple sets of arguments, or to evaluate a single set of 88 | arguments multiple times, 89 | .Va optind 90 | must be initialized to the number of argv entries to be skipped in each 91 | evaluation. 92 | .Pp 93 | The 94 | .Fn getopt 95 | function 96 | returns an 97 | .Dv EOF 98 | when the argument list is exhausted, or a non-recognized 99 | option is encountered. 100 | The interpretation of options in the argument list may be cancelled 101 | by the option 102 | .Ql -- 103 | (double dash) which causes 104 | .Fn getopt 105 | to signal the end of argument processing and return an 106 | .Dv EOF . 107 | When all options have been processed (i.e., up to the first non-option 108 | argument), 109 | .Fn getopt 110 | returns 111 | .Dv EOF . 112 | .Sh DIAGNOSTICS 113 | If the 114 | .Fn getopt 115 | function encounters a character not found in the string 116 | .Va optarg 117 | or detects 118 | a missing option argument 119 | it writes error message 120 | .Ql ? 121 | to the 122 | .Em stderr . 123 | Setting 124 | .Va opterr 125 | to a zero will disable these error messages. 126 | .Sh EXAMPLE 127 | .Bd -literal -compact 128 | extern char *optarg; 129 | extern int optind; 130 | int bflag, ch, fd; 131 | bflag = 0; 132 | while ((ch = getopt(argc, argv, "bf:")) != EOF) 133 | switch(ch) { 134 | case 'b': 135 | bflag = 1; 136 | break; 137 | case 'f': 138 | if ((fd = open(optarg, O_RDONLY, 0)) < 0) { 139 | (void)fprintf(stderr, 140 | "myname: unable to read file %s.\en", optarg); 141 | exit(1) ; 142 | } 143 | break; 144 | case '?': 145 | default: 146 | usage(); 147 | } 148 | argc -= optind; 149 | argv += optind; 150 | .Ed 151 | .Sh HISTORY 152 | The 153 | .Fn getopt 154 | function appeared 155 | .Bx 4.3 . 156 | .Sh BUGS 157 | Option arguments are allowed to begin with 158 | .Dq Li \- ; 159 | this is reasonable but 160 | reduces the amount of error checking possible. 161 | .Pp 162 | A single dash 163 | .Dq Li - 164 | may be specified as an character in 165 | .Fa optstring , 166 | however it should 167 | .Em never 168 | have an argument associated with it. 169 | This allows 170 | .Fn getopt 171 | to be used with programs that expect 172 | .Dq Li - 173 | as an option flag. 174 | This practice is wrong, and should not be used in any current development. 175 | It is provided for backward compatibility 176 | .Em only . 177 | By default, a single dash causes 178 | .Fn getopt 179 | to return 180 | .Dv EOF . 181 | This is, we believe, compatible with System V. 182 | .Pp 183 | It is also possible to handle digits as option letters. 184 | This allows 185 | .Fn getopt 186 | to be used with programs that expect a number 187 | .Pq Dq Li \&-\&3 188 | as an option. 189 | This practice is wrong, and should not be used in any current development. 190 | It is provided for backward compatibility 191 | .Em only . 192 | The following code fragment works fairly well. 193 | .Bd -literal -offset indent 194 | int length; 195 | char *p; 196 | while ((c = getopt(argc, argv, "0123456789")) != EOF) 197 | switch (c) { 198 | case '0': case '1': case '2': case '3': case '4': 199 | case '5': case '6': case '7': case '8': case '9': 200 | p = argv[optind - 1]; 201 | if (p[0] == '-' && p[1] == ch && !p[2]) 202 | length = atoi(++p); 203 | else 204 | length = atoi(argv[optind] + 1); 205 | break; 206 | } 207 | } 208 | .Ed 209 | -------------------------------------------------------------------------------- /getopt.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1987 Regents of the University of California. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. All advertising materials mentioning features or use of this software 14 | * must display the following acknowledgement: 15 | * This product includes software developed by the University of 16 | * California, Berkeley and its contributors. 17 | * 4. Neither the name of the University nor the names of its contributors 18 | * may be used to endorse or promote products derived from this software 19 | * without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 | * SUCH DAMAGE. 32 | */ 33 | 34 | /* HITECH C does not have index() or rindex() so we replace these with 35 | * strchr() and strrchr(), which it does support. 36 | * support const 37 | */ 38 | 39 | #include 40 | 41 | #define index(s,c) strchr(s,c) 42 | #define rindex(s,c) strrchr(s,c) 43 | 44 | /* Just make sure that sccsid is completely gone when using HITECH C - 45 | * because every byte is precious. 46 | */ 47 | #if defined(LIBC_SCCS) && !defined(lint) 48 | static char sccsid[] = "@(#)getopt.c 4.13 (Berkeley) 2/23/91"; 49 | #endif /* LIBC_SCCS and not lint */ 50 | 51 | #include 52 | #include 53 | #include 54 | /* 55 | * get option letter from argument vector 56 | */ 57 | int opterr = 1, /* if error message should be printed */ 58 | optind = 1, /* index into parent argv vector */ 59 | optopt; /* character checked for validity */ 60 | char *optarg; /* argument associated with option */ 61 | #define BADCH (int)'?' 62 | #define EMSG "" 63 | int 64 | getopt(nargc, nargv, ostr) 65 | int nargc; 66 | char * const *nargv; 67 | const char *ostr; 68 | { 69 | static char *place = EMSG; /* option letter processing */ 70 | register char *oli; /* option letter list index */ 71 | char *p; 72 | if (!*place) { /* update scanning pointer */ 73 | if (optind >= nargc || *(place = nargv[optind]) != '-') { 74 | place = EMSG; 75 | return(EOF); 76 | } 77 | if (place[1] && *++place == '-') { /* found "--" */ 78 | ++optind; 79 | place = EMSG; 80 | return(EOF); 81 | } 82 | } /* option letter okay? */ 83 | if ((optopt = (int)*place++) == (int)':' || 84 | !(oli = index(ostr, optopt))) { 85 | /* 86 | * if the user didn't specify '-' as an option, 87 | * assume it means EOF. 88 | */ 89 | if (optopt == (int)'-') 90 | return(EOF); 91 | if (!*place) 92 | ++optind; 93 | if (opterr) { 94 | if (!(p = rindex(*nargv, '/'))) 95 | p = *nargv; 96 | else 97 | ++p; 98 | (void)fprintf(stderr, "%s: illegal option -- %c\n", 99 | p, optopt); 100 | } 101 | return(BADCH); 102 | } 103 | if (*++oli != ':') { /* don't need argument */ 104 | optarg = NULL; 105 | if (!*place) 106 | ++optind; 107 | } 108 | else { /* need an argument */ 109 | if (*place) /* no white space */ 110 | optarg = place; 111 | else if (nargc <= ++optind) { /* no arg */ 112 | place = EMSG; 113 | if (!(p = rindex(*nargv, '/'))) 114 | p = *nargv; 115 | else 116 | ++p; 117 | if (opterr) 118 | (void)fprintf(stderr, 119 | "%s: option requires an argument -- %c\n", 120 | p, optopt); 121 | return(BADCH); 122 | } 123 | else /* white space */ 124 | optarg = nargv[optind]; 125 | place = EMSG; 126 | ++optind; 127 | } 128 | return(optopt); /* dump back option letter */ 129 | } 130 | 131 | -------------------------------------------------------------------------------- /getopt.h: -------------------------------------------------------------------------------- 1 | /* getopt.h 2 | * 3 | * Note that HI-TECH C does not have this function; _getopt() 4 | * is different; it is used to set argc and argv (and av[0] 5 | * with the program name). 6 | * 7 | * We take getopt.c from BSD, to avoid "reinventing the wheel". 8 | * This provides us with a "standard" way to scan arguments. 9 | */ 10 | 11 | extern int opterr, 12 | optind, 13 | optopt; 14 | extern char *optarg; 15 | int getopt(int nargc, char **nargv, char *ostr); 16 | -------------------------------------------------------------------------------- /howto.txt: -------------------------------------------------------------------------------- 1 | How to install 2 | 3 | am9511 is meant to be installed into an emulator. Here, we integrate 4 | am9511 into Zxcc and RunCPM. 5 | 6 | 7 | Integrating into Zxcc 8 | ===================== 9 | 10 | We use zxcc here as an example: 11 | 12 | https://www.seasip.info/Unix/Zxcc/ 13 | 14 | I use zxcc to cross compile HI-TECH C, and do "light testing". 15 | zxcc will run mbasic 16 | 17 | : fred@tara bin $; zxcc mbasic 18 | BASIC-80 Rev. 5.21 19 | [CP/M Version] 20 | Copyright 1977-1981 (C) by Microsoft 21 | Created: 28-Jul-81 22 | 39480 Bytes free 23 | Ok 24 | 25 | and I want to use MBASIC as a test harness. So, zxcc becomes the 26 | first target. First, download and build zxcc, and make sure that 27 | it works. 28 | 29 | In directory zxcc-0.5.7/bin 30 | 31 | Add files 32 | am9511.c 33 | am9511.h 34 | ansi.h 35 | floatcnv.c 36 | flaatcnv.h 37 | ova.c 38 | ova.h 39 | types.h 40 | 41 | from the am9511 project. 42 | 43 | Modify the Makefile: 44 | 45 | - add "-lm" to LIBS 46 | - add "am9511.$(OBJEXT) ova.$(OBJEXT) floatcnv.$(OBJEXT)" to 47 | am_zxcc_OBJECTS 48 | - add -I. to CPPFLAGS (? may not be needed) 49 | 50 | Edit zxcc.c 51 | 52 | Replace: 53 | 54 | unsigned int in() { return 0; } 55 | unsigned int out() { return 0; } 56 | 57 | with 58 | 59 | /* support AM9511 60 | * 61 | * The interface is very simple -- if interrupts are not used. 62 | * 63 | * Note the values for AM_STATUS and AM_DATA are to support 64 | * rc2014 (planeta.com) 65 | */ 66 | 67 | void *am9511 = NULL; 68 | 69 | #define AM_STATUS 0x43 70 | #define AM_DATA 0x42 71 | 72 | unsigned int in(tstates,a,v) { 73 | unsigned int r = 0; 74 | if (am9511 == NULL) 75 | am9511 = am_create(AM_STATUS, AM_DATA); 76 | if (v == AM_DATA) 77 | r = am_pop(am9511); 78 | else if (v == AM_STATUS) 79 | r = am_status(am9511); 80 | return r; 81 | } 82 | 83 | unsigned int out(tstates,a,v,a2) { 84 | if (am9511 == NULL) 85 | am9511 = am_create(AM_STATUS, AM_DATA); 86 | if (v == AM_DATA) 87 | am_push(am9511, a2); 88 | else if (v == AM_STATUS) 89 | am_command(am_9511, a2); 90 | return 0; 91 | } 92 | 93 | 94 | And, add 95 | 96 | #include "am9511.h" 97 | 98 | to the top of the file 99 | 100 | "make" and "make install" 101 | 102 | Now, zxcc should have the AM9511 emulator, as ports 66 and 67. 103 | 104 | See AM9511.BAS for an MBASIC program that is the start of a test. 105 | 106 | As am9511 matures, additional instructions on incorporating into 107 | other emulators should be given. Not ready yet... so be careful. 108 | 109 | Please note that there is an error in Zxcc -- in edops.h opcodes 110 | 0xed 0x70 and 0xed 0x71 are wrong: replace with 111 | 112 | instr(0x70,8); 113 | {unsigned char x;input(x); 114 | store(hl,x); 115 | } 116 | endinstr; 117 | 118 | instr(0x71,8); 119 | {unsigned char x=fetch(hl); 120 | tstates+=out(tstates,b,c,x); 121 | } 122 | endinstr; 123 | 124 | Note that this has NO effect on running zxc, etc. Just using Zxcc 125 | as an emulator driving i/o devices like am9511. 126 | 127 | 128 | Integrating into RunCPM 129 | ======================= 130 | 131 | https://github.com/MockbaTheBorg/RunCPM 132 | 133 | Download, build, and test RunCPM. After this -- 134 | 135 | Add files 136 | am9511.c 137 | am9511.h 138 | ansi.h 139 | floatcnv.c 140 | flaatcnv.h 141 | ova.c 142 | ova.h 143 | 144 | to RunCPM-master/RunCPM 145 | 146 | Edit cpu.h: 147 | 148 | Add #include "am9511.h" at the top 149 | 150 | Find cpu_in and cpu_out (Functions needed by the soft CPU implementation) and 151 | replace with: (Note port assignments 0x43 and 0x42 are for rc2014 / planeta.com 152 | compatibilty) 153 | 154 | /* AM9511 */ 155 | #define AM_STATUS 0x43 156 | #define AM_DATA 0x42 157 | 158 | void *am9511 = NULL; 159 | 160 | void cpu_out(const uint32 Port, const uint32 Value) { 161 | if (am9511 == NULL) am9511 = am_create(AM_STATUS, AM_DATA); 162 | if (Port == AM_DATA) am_push(am9511, Value); 163 | else if (Port == AM_STATUS) am_command(am9511, Value); 164 | else _Bios(); 165 | } 166 | 167 | uint32 cpu_in(const uint32 Port) { 168 | if (am9511 == NULL) am9511 = am_create(AM_STATUS, AM_DATA); 169 | if (Port == AM_DATA) return am_pop(am9511); 170 | else if (Port == AM_STATUS) return am_status(am9511); 171 | _Bdos(); 172 | return(HIGH_REGISTER(AF)); 173 | } 174 | 175 | Rebuild (make posix build). RunCPM should now include am9511 at ports 66 176 | and 67. 177 | 178 | For both Zxcc and RunCPM, MBASIC can be used with AM9511.BAS 179 | 180 | planeta.com can be used to test am9511 under RunCPM or Zxcc. 181 | 182 | https://github.com/feilipu/planets 183 | 184 | for the source code for planeta.com and planet32.com planeta.com is 185 | compiled with z88dk for AM9511A, planet32.com is compiled for ieee 186 | 32 bit float. 187 | 188 | 189 | -------------------------------------------------------------------------------- /hw9511.c: -------------------------------------------------------------------------------- 1 | /* hw9511.c 2 | * 3 | * Hardware for am9511. Link with this instead of am9511 to use actual 4 | * am9511 chip. 5 | */ 6 | 7 | #include 8 | #include 9 | #ifdef z80 10 | #include 11 | #endif 12 | 13 | 14 | struct am9511 { 15 | int status; 16 | int data; 17 | }; 18 | 19 | 20 | /* If we use the HI-TECH C functions inp() and outp() note that 21 | * they use Z80 instructions for i/o -- these are NOT supported 22 | * by Zxcc (and maybe not by RunCPM). 23 | * 24 | * So, we use HI-TECH C in-line assembler. Yes, this *is* 25 | * self-modifying code. But, doing the i/o this way works with 26 | * Zxcc. 27 | * 28 | * DO NOT OPTIMIZE WHEN COMPILING 29 | * 30 | * THIS IS NOT RE-ENTRANT. NEED TO FIX THAT LATER. 31 | */ 32 | #ifdef z80 33 | 34 | static char io_port; 35 | static char io_data; 36 | 37 | int inp(int port) { 38 | io_port = (char)port; 39 | #asm 40 | ld a,(_io_port) 41 | ld (mod1),a 42 | defb 0dbh 43 | mod1: 44 | defb 0 45 | ld (_io_data),a 46 | #endasm 47 | return io_data; 48 | } 49 | 50 | void outp(int port, int data) { 51 | io_port = (char)port; 52 | io_data = (char)data; 53 | #asm 54 | ld a,(_io_port) 55 | ld (mod2),a 56 | ld a,(_io_data) 57 | defb 0d3h 58 | mod2: 59 | defb 0 60 | #endasm 61 | } 62 | 63 | #else 64 | 65 | #pragma GCC warning "inp and outp are dummy functions" 66 | #define inp(port) 0 67 | #define outp(port,data) 68 | 69 | #endif 70 | 71 | 72 | /* Push byte to am9511 stack 73 | */ 74 | void am_push(void *p, unsigned char n) { 75 | outp(((struct am9511 *)p)->data, n); 76 | } 77 | 78 | 79 | /* Pop byte from am9511 stack 80 | */ 81 | unsigned char am_pop(void *p) { 82 | return inp(((struct am9511 *)p)->data); 83 | } 84 | 85 | 86 | /* Return am9511 status 87 | */ 88 | unsigned char am_status(void *p) { 89 | return inp(((struct am9511 *)p)->status); 90 | } 91 | 92 | 93 | /* Send command to am9511 94 | */ 95 | void am_command(void *p, unsigned char n) { 96 | outp(((struct am9511 *)p)->status, n); 97 | } 98 | 99 | 100 | /* Reset am9511 -- set status and data ports 101 | * 102 | * If -ve value passed for port, use default value. The 103 | * default value is set to match with the Zxcc and RunCPM 104 | * emulators. 105 | */ 106 | void am_reset(void *p) { 107 | p = p; 108 | } 109 | 110 | 111 | /* Dump am9511 stack 112 | */ 113 | void am_dump(void *p, unsigned char op) { 114 | op = op; 115 | p = p; 116 | } 117 | 118 | 119 | /* Create AM9511 access structure 120 | */ 121 | void *am_create(int status, int data) { 122 | struct am9511 *p; 123 | p = malloc(sizeof (struct am9511)); 124 | if (p == NULL) 125 | return NULL; 126 | p->status = 0x51; 127 | p->data = 0x50; 128 | if (status >= 0) 129 | p->status = status; 130 | if (data >= 0) 131 | p->data = data; 132 | return p; 133 | } 134 | -------------------------------------------------------------------------------- /ova.c: -------------------------------------------------------------------------------- 1 | /* ova.c 2 | * 3 | * Not an egg -- overflow arithmetic. 16 and 32 bit. 4 | * 5 | * Designed to work with both gcc and hi-tech C. This is then meant 6 | * to allow operation of AM9511 emulation with hi-tech C natively 7 | * on z80 platform. There are faster ways, but we want to be able 8 | * to validate the code against a real AM9511A, as long as some of 9 | * those chips are still operational. 10 | * 11 | * Because this code is meant to be (potentially) used with 12 | * hi-tech C, and other very old environments, external names are 13 | * unique to 5 characters (6 with prepended '_'). 14 | */ 15 | 16 | #include 17 | 18 | #include "ova.h" 19 | #include "types.h" 20 | 21 | 22 | #define USE_MUL16 23 | 24 | 25 | /* Constant 1, good for 16, 32 and 64 bit 26 | */ 27 | static unsigned char one[] = { 0x01, 0x00, 0x00, 0x00, 28 | 0x00, 0x00, 0x00, 0x00 }; 29 | 30 | 31 | /* Return overflow flag after add16(). 32 | * 33 | * Overflow is detected by sign bits of the arguments and result. 34 | * This can be done after the add (or subtract) is done. That is 35 | * why add and subtract return carry, and leave overflow to a 36 | * separate function. 37 | */ 38 | int oadd16(unsigned char *pa, 39 | unsigned char *pb, 40 | unsigned char *pc) { 41 | unsigned char sa, sb, sc; 42 | int overflow = 0; 43 | 44 | sa = pa[1] & 0x80; 45 | sb = pb[1] & 0x80; 46 | sc = pc[1] & 0x80; 47 | if ((sa == 0x00) && (sb == 0x00) && (sc == 0x80)) 48 | overflow = 1; 49 | if ((sa == 0x80) && (sb == 0x80) && (sc == 0x00)) 50 | overflow = 1; 51 | return overflow; 52 | } 53 | 54 | 55 | /* Return overflow flag after add32(). 56 | */ 57 | int oadd32(unsigned char *pa, 58 | unsigned char *pb, 59 | unsigned char *pc) { 60 | unsigned char sa, sb, sc; 61 | int overflow = 0; 62 | 63 | sa = pa[3] & 0x80; 64 | sb = pb[3] & 0x80; 65 | sc = pc[3] & 0x80; 66 | if ((sa == 0x00) && (sb == 0x00) && (sc == 0x80)) 67 | overflow = 1; 68 | if ((sa == 0x80) && (sb == 0x80) && (sc == 0x00)) 69 | overflow = 1; 70 | return overflow; 71 | } 72 | 73 | 74 | /* Return overflow flag after sub16(). 75 | */ 76 | int osub16(unsigned char *pa, 77 | unsigned char *pb, 78 | unsigned char *pc) { 79 | unsigned char sa, sb, sc; 80 | int overflow = 0; 81 | 82 | sa = pa[1] & 0x80; 83 | sb = pb[1] & 0x80; 84 | sc = pc[1] & 0x80; 85 | if ((sa == 0x00) && (sb == 0x80) && (sc == 0x80)) 86 | overflow = 1; 87 | if ((sa == 0x80) && (sb == 0x00) && (sc == 0x00)) 88 | overflow = 1; 89 | return overflow; 90 | } 91 | 92 | 93 | /* Return overflow after sub32(). 94 | */ 95 | int osub32(unsigned char *pa, 96 | unsigned char *pb, 97 | unsigned char *pc) { 98 | unsigned char sa, sb, sc; 99 | int overflow = 0; 100 | 101 | sa = pa[3] & 0x80; 102 | sb = pb[3] & 0x80; 103 | sc = pc[3] & 0x80; 104 | if ((sa == 0x00) && (sb == 0x80) && (sc == 0x80)) 105 | overflow = 1; 106 | if ((sa == 0x80) && (sb == 0x00) && (sc == 0x00)) 107 | overflow = 1; 108 | return overflow; 109 | } 110 | 111 | 112 | /* 16 bit add, returns carry. 113 | * 114 | * All parameters are little endian. 115 | */ 116 | int add16(unsigned char *pa, 117 | unsigned char *pb, 118 | unsigned char *pc) { 119 | uint16 a, b, c; 120 | int carry; 121 | 122 | a = pa[0] | (pa[1] << 8); 123 | b = pb[0] | (pb[1] << 8); 124 | c = a + b; 125 | carry = (c < a) || (c < b); 126 | pc[0] = c; 127 | pc[1] = c >> 8; 128 | return carry; 129 | } 130 | 131 | 132 | /* 32 bit add, returns carry. Uses add16(). 133 | */ 134 | int add32(unsigned char *pa, 135 | unsigned char *pb, 136 | unsigned char *pc) { 137 | unsigned char *pah, *pbh, *pch; 138 | int carry, c2; 139 | 140 | pah = pa + 2; 141 | pbh = pb + 2; 142 | pch = pc + 2; 143 | 144 | carry = add16(pa, pb, pc); 145 | if (!carry) { 146 | carry = add16(pah, pbh, pch); 147 | return carry; 148 | } 149 | 150 | carry = add16(one, pbh, pch); 151 | c2 = add16(pch, pah, pch); 152 | return carry || c2; 153 | } 154 | 155 | 156 | /* 64 bit add, returns carry. Uses add32(). 157 | * 158 | * This supports 32x32->64 bit multiply. 159 | */ 160 | static int add64(unsigned char *pa, 161 | unsigned char *pb, 162 | unsigned char *pc) { 163 | unsigned char *pah, *pbh, *pch; 164 | int carry, c2; 165 | 166 | pah = pa + 4; 167 | pbh = pb + 4; 168 | pch = pc + 4; 169 | 170 | carry = add32(pa, pb, pc); 171 | if (!carry) { 172 | carry = add32(pah, pbh, pch); 173 | return carry; 174 | } 175 | 176 | carry = add32(one, pbh, pch); 177 | c2 = add32(pch, pah, pch); 178 | return carry || c2; 179 | } 180 | 181 | 182 | /* 16 bit 2's complement, return 1 if 0x8000 183 | */ 184 | int cm16(unsigned char *pa, 185 | unsigned char *pb) { 186 | uint16 a, b; 187 | int r = 0; 188 | 189 | a = pa[0] | (pa[1] << 8); 190 | if (a == 0x8000) { 191 | b = a; 192 | r = 1; 193 | } else { 194 | b = ~a; 195 | ++b; 196 | } 197 | pb[0] = b; 198 | pb[1] = b >> 8; 199 | return r; 200 | } 201 | 202 | 203 | /* 32 bit 2's complement, return 1 if 0x80000000 204 | */ 205 | int cm32(unsigned char *pa, 206 | unsigned char *pb) { 207 | uint16 a, ah, b, bh; 208 | int r = 0; 209 | 210 | a = pa[0] | (pa[1] << 8); 211 | ah = pa[2] | (pa[3] << 8); 212 | if ((a == 0x0000) && (ah == 0x8000)) { 213 | b = a; 214 | bh = ah; 215 | r = 1; 216 | } else { 217 | b = ~a; 218 | bh = ~ah; 219 | if (++b == 0) 220 | ++bh; 221 | } 222 | pb[0] = b; 223 | pb[1] = b >> 8; 224 | pb[2] = bh; 225 | pb[3] = bh >> 8; 226 | return r; 227 | } 228 | 229 | 230 | /* 64 bit 2's complement, return 1 if 0x8000 0000 0000 0000 231 | */ 232 | static int cm64(unsigned char *pa, 233 | unsigned char *pb) { 234 | if (pa[7] == 0x80) 235 | if ((pa[0] | pa[1] | pa[2] | pa[3] | 236 | pa[4] | pa[5] | pa[6]) == 0) { 237 | pb[0] = pb[1] = pb[2] = pb[3] = pb[4] = pb[5] = pb[6] = 0; 238 | pb[7] = 0x80; 239 | return 1; 240 | } 241 | 242 | pb[0] = ~pa[0]; 243 | pb[1] = ~pa[1]; 244 | pb[2] = ~pa[2]; 245 | pb[3] = ~pa[3]; 246 | pb[4] = ~pa[4]; 247 | pb[5] = ~pa[5]; 248 | pb[6] = ~pa[6]; 249 | pb[7] = ~pa[7]; 250 | add64(pb, one, pb); 251 | return 0; 252 | } 253 | 254 | 255 | /* 16 bit subtract. Return 1 if carry. 256 | */ 257 | int sub16(unsigned char *pa, 258 | unsigned char *pb, 259 | unsigned char *pc) { 260 | uint16 a, b, c; 261 | int carry; 262 | 263 | a = pa[0] | (pa[1] << 8); 264 | b = pb[0] | (pb[1] << 8); 265 | c = a - b; 266 | if (a == 0x8000) 267 | carry = 1; 268 | else 269 | carry = a < b; 270 | pc[0] = c; 271 | pc[1] = c >> 8; 272 | return carry; 273 | } 274 | 275 | 276 | /* 32 subtract. Return carry. 277 | */ 278 | int sub32(unsigned char *pa, 279 | unsigned char *pb, 280 | unsigned char *pc) { 281 | unsigned char *pah, *pbh, *pch; 282 | int carry, c2; 283 | 284 | pah = pa + 2; 285 | pbh = pb + 2; 286 | pch = pc + 2; 287 | 288 | carry = sub16(pa, pb, pc); 289 | if (!carry) { 290 | carry = sub16(pah, pbh, pch); 291 | return carry; 292 | } 293 | 294 | carry = sub16(pah, one, pch); 295 | c2 = sub16(pch, pbh, pch); 296 | return carry || c2; 297 | } 298 | 299 | 300 | /* 16x16 giving 32 bit multiplication 301 | * 302 | * We break it down into 8x8 giving 16 bit 303 | * 304 | * [mHigh mLow] 305 | * * [nHigh nLow] 306 | * ------------ 307 | * [mHigh * nLow] [mLow * nLow] 308 | * + [mHigh * nHigh] [mLow * nHigh] 309 | * -------------------------------------------- 310 | */ 311 | 312 | #ifndef USE_MUL16 313 | /* Multiply 8x8->16 bit result. Hopefully, we have hardware 314 | * which does this. The Z80 does not. But most of its successors 315 | * do, and most gcc platforms do. 316 | */ 317 | static uint16 mul8(unsigned char m, unsigned char n) { 318 | return m * n; 319 | } 320 | #endif 321 | 322 | /* Multiply 16x16->32 323 | * 324 | * r may be one or both of the operands 325 | */ 326 | static void mul16(unsigned char *m, unsigned char *n, unsigned char *r) { 327 | #ifdef USE_MUL16 328 | /* If we have 16x16->32 multiply, use it. 329 | */ 330 | uint32 a, b, c; 331 | 332 | a = m[0] + (m[1] << 8); 333 | b = n[0] + (n[1] << 8); 334 | c = a * b; 335 | r[0] = c & 0xff; 336 | r[1] = (c >> 8) & 0xff; 337 | r[2] = (c >> 16) & 0xff; 338 | r[3] = (c >> 24) & 0xff; 339 | #else 340 | /* Build 16x16->32 from 8x8->16 bit multiply 341 | */ 342 | unsigned char mLow = m[0]; 343 | unsigned char mHigh = m[1]; 344 | 345 | unsigned char nLow = n[0]; 346 | unsigned char nHigh = n[1]; 347 | 348 | uint16 mLow_nLow = mul8(mLow, nLow); 349 | uint16 mHigh_nLow = mul8(mHigh, nLow); 350 | uint16 mLow_nHigh = mul8(mLow, nHigh); 351 | uint16 mHigh_nHigh = mul8(mHigh, nHigh); 352 | 353 | uint16 a; 354 | int carry; 355 | unsigned char r2[4], r3[4], r4[4]; 356 | int i; 357 | 358 | for (i = 0; i < 4; ++i) 359 | r2[i] = r3[i] = r[4] = 0; 360 | 361 | /* r2: 362 | * 363 | * 3 364 | * 2 365 | * 1 | mLow_nLow 366 | * 0 | 367 | */ 368 | r2[0] = mLow_nLow & 0xff; 369 | r2[1] = (mLow_nLow >> 8) & 0xff; 370 | 371 | /* r3: 372 | * 373 | * 3 374 | * 2 | mHigh_nLow + mLow_nHigh 375 | * 1 | 376 | * 0 377 | */ 378 | carry = add16((unsigned char *)&mHigh_nLow, 379 | (unsigned char *)&mLow_nHigh, 380 | r3 + 1); 381 | 382 | /* r4: 383 | * 384 | * 3 | mHigh_nHigh (+ carry) 385 | * 2 | 386 | * 1 387 | * 0 388 | */ 389 | a = mHigh_nHigh + carry; 390 | r4[2] = a & 0xff; 391 | r4[3] = (a >> 8) & 0xff; 392 | 393 | /* r = r2 + r3 394 | */ 395 | add32(r2, r3, r); 396 | 397 | /* r = r + r4 */ 398 | add32(r, r4, r); 399 | #endif 400 | } 401 | 402 | 403 | /* 16 bit multiply, lower. Uses mul16(). Returns overflow. 404 | * (1 if high 16 is non-zero). 405 | */ 406 | int mull16(unsigned char *pa, 407 | unsigned char *pb, 408 | unsigned char *pc) { 409 | unsigned char r[4]; 410 | unsigned char a[2], b[2]; 411 | int s, o; 412 | 413 | if (((pa[0] == 0x00) && (pa[1] == 0x80)) || 414 | ((pb[0] == 0x00) && (pb[1] == 0x80))) { 415 | pc[0] = 0x00; 416 | pc[1] = 0x80; 417 | return 1; 418 | } 419 | 420 | /* We know that neither operand is 0x8000, so change sign will 421 | * work. 422 | */ 423 | s = 0; 424 | if (pa[1] & 0x80) { 425 | s ^= 1; 426 | cm16(pa, a); 427 | } else { 428 | a[0] = pa[0]; 429 | a[1] = pa[1]; 430 | } 431 | if (pb[1] & 0x80) { 432 | s ^= 1; 433 | cm16(pb, b); 434 | } else { 435 | b[0] = pb[0]; 436 | b[1] = pb[1]; 437 | } 438 | 439 | mul16(a, b, r); 440 | 441 | o = (r[2] | r[3]) != 0; 442 | 443 | if (s) 444 | o |= cm32(r, r); 445 | 446 | pc[0] = r[0]; 447 | pc[1] = r[1]; 448 | 449 | return o; 450 | } 451 | 452 | 453 | /* 16 bit multiply, upper. Uses mul16(). returns overflow. 454 | */ 455 | int mulu16(unsigned char *pa, 456 | unsigned char *pb, 457 | unsigned char *pc) { 458 | unsigned char r[4]; 459 | unsigned char a[2], b[2]; 460 | int s, o; 461 | 462 | if (((pa[0] == 0x00) && (pa[1] == 0x80)) || 463 | ((pb[0] == 0x00) && (pb[1] == 0x80))) { 464 | pc[0] = 0x00; 465 | pc[1] = 0x80; 466 | return 1; 467 | } 468 | 469 | s = 0; 470 | if (pa[1] & 0x80) { 471 | s ^= 1; 472 | cm16(pa, a); 473 | } else { 474 | a[0] = pa[0]; 475 | a[1] = pa[1]; 476 | } 477 | if (pb[1] & 0x80) { 478 | s ^= 1; 479 | cm16(pb, b); 480 | } else { 481 | b[0] = pb[0]; 482 | b[1] = pb[1]; 483 | } 484 | 485 | mul16(a, b, r); 486 | 487 | o = 0; 488 | if (s) 489 | o = cm32(r, r); 490 | 491 | pc[0] = r[2]; 492 | pc[1] = r[3]; 493 | 494 | return o; 495 | } 496 | 497 | 498 | /* 16 bit division. 499 | * 500 | * Eventually, I will put my own code in here... but, the objective 501 | * is to have the emulator running. 502 | */ 503 | int div16(unsigned char *pa, 504 | unsigned char *pb, 505 | unsigned char *pc) { 506 | int16 a, b, c; 507 | int r = 0; 508 | 509 | a = pa[0] | 510 | (pa[1] << 8); 511 | b = pb[0] | 512 | (pb[1] << 8); 513 | if (b == 0) { 514 | c = b; 515 | r = 1; 516 | } else 517 | c = a / b; 518 | pc[0] = c & 0xff; 519 | pc[1] = (c >> 8) & 0xff; 520 | return r; 521 | } 522 | 523 | 524 | /* 32 bit division. 525 | * 526 | * Eventually, I will put my own code in here... but, the objective 527 | * is to have the emulator running. 528 | */ 529 | int div32(unsigned char *pa, 530 | unsigned char *pb, 531 | unsigned char *pc) { 532 | int32 a, b, c; 533 | int r = 0; 534 | 535 | a = pa[0] | 536 | (pa[1] << 8) | 537 | (pa[2] << 16) | 538 | (pa[3] << 24); 539 | b = pb[0] | 540 | (pb[1] << 8) | 541 | (pb[2] << 16) | 542 | (pb[3] << 24); 543 | if (b == 0) { 544 | c = b; 545 | r = 1; 546 | } else 547 | c = a / b; 548 | pc[0] = c & 0xff; 549 | pc[1] = (c >> 8) & 0xff; 550 | pc[2] = (c >> 16) & 0xff; 551 | pc[3] = (c >> 24) & 0xff; 552 | return r; 553 | } 554 | 555 | 556 | /* 32x32->64 multiply 557 | */ 558 | static void mul32(unsigned char *pa, 559 | unsigned char *pb, 560 | unsigned char *pc) { 561 | unsigned char *pah, *pal; 562 | unsigned char *pbh, *pbl; 563 | 564 | unsigned char r0[8]; 565 | unsigned char r1[8]; 566 | unsigned char r2[8]; 567 | unsigned char r3[8]; 568 | 569 | int i; 570 | 571 | for (i = 0; i < 8; ++i) 572 | pc[i] = r0[i] = r1[i] = r2[i] = r3[i] = 0; 573 | 574 | pal = pa; 575 | pah = pa + 2; 576 | pbl = pb; 577 | pbh = pb + 2; 578 | 579 | mul16(pbl, pal, r0); 580 | mul16(pbl, pah, r1 + 2); 581 | mul16(pbh, pal, r2 + 2); 582 | mul16(pbh, pah, r3 + 4); 583 | add64(r0, r1, pc); 584 | add64(pc, r2, r0); 585 | add64(r0, r3, pc); 586 | } 587 | 588 | 589 | int mull32(unsigned char *pa, 590 | unsigned char *pb, 591 | unsigned char *pc) { 592 | unsigned char r[8]; 593 | unsigned char a[4], b[4]; 594 | int s, o; 595 | 596 | if (((pa[0]==0x00) && (pa[1]==0x00) && (pa[2]==0x00) && (pa[3]==0x80)) || 597 | ((pb[0]==0x00) && (pb[1]==0x00) && (pa[2]==0x00) && (pa[3]==0x80))) { 598 | pc[0] = 0x00; 599 | pc[1] = 0x00; 600 | pc[2] = 0x00; 601 | pc[3] = 0x80; 602 | return 1; 603 | } 604 | 605 | s = 0; 606 | if (pa[3] & 0x80) { 607 | s ^= 1; 608 | cm32(pa, a); 609 | } else { 610 | a[0] = pa[0]; 611 | a[1] = pa[1]; 612 | a[2] = pa[2]; 613 | a[3] = pa[3]; 614 | } 615 | if (pb[3] & 0x80) { 616 | s ^= 1; 617 | cm32(pb, b); 618 | } else { 619 | b[0] = pb[0]; 620 | b[1] = pb[1]; 621 | b[2] = pb[2]; 622 | b[3] = pb[3]; 623 | } 624 | 625 | mul32(a, b, r); 626 | 627 | o = (r[4] | r[5] | r[6] | r[7]) != 0; 628 | 629 | if (s) 630 | o |= cm64(r, r); 631 | 632 | pc[0] = r[0]; 633 | pc[1] = r[1]; 634 | pc[2] = r[2]; 635 | pc[3] = r[3]; 636 | 637 | return o; 638 | } 639 | 640 | 641 | int mulu32(unsigned char *pa, 642 | unsigned char *pb, 643 | unsigned char *pc) { 644 | unsigned char r[8]; 645 | unsigned char a[4], b[4]; 646 | int s, o; 647 | 648 | if (((pa[0]==0x00) && (pa[1]==0x00) && (pa[2]==0x00) && (pa[3]==0x80)) || 649 | ((pb[0]==0x00) && (pb[1]==0x00) && (pa[2]==0x00) && (pa[3]==0x80))) { 650 | pc[0] = 0x00; 651 | pc[1] = 0x00; 652 | pc[2] = 0x00; 653 | pc[3] = 0x80; 654 | return 1; 655 | } 656 | 657 | s = 0; 658 | if (pa[3] & 0x80) { 659 | s ^= 1; 660 | cm32(pa, a); 661 | } else { 662 | a[0] = pa[0]; 663 | a[1] = pa[1]; 664 | a[2] = pa[2]; 665 | a[3] = pa[3]; 666 | } 667 | if (pb[3] & 0x80) { 668 | s ^= 1; 669 | cm32(pb, b); 670 | } else { 671 | b[0] = pb[0]; 672 | b[1] = pb[1]; 673 | b[2] = pb[2]; 674 | b[3] = pb[3]; 675 | } 676 | 677 | mul32(a, b, r); 678 | 679 | o = 0; 680 | if (s) 681 | o = cm64(r, r); 682 | 683 | pc[0] = r[4]; 684 | pc[1] = r[5]; 685 | pc[2] = r[6]; 686 | pc[3] = r[7]; 687 | 688 | return o; 689 | } 690 | 691 | 692 | /* 693 | 16B,16B (32-bit multiplicand) 694 | * 16B,16B (32-bit multiplier) 695 | ----------------- 696 | 16B,16B (32-bit partial product) 697 | + 16B,16B (32-bit partial product) 698 | + 16B,16B (32-bit partial product) 699 | + 16B,16B (32-bit partial product) 700 | ================= 701 | 16B,16B,16B,16B (64-bit product) 702 | 703 | */ 704 | -------------------------------------------------------------------------------- /ova.h: -------------------------------------------------------------------------------- 1 | /* ova.h 2 | * 3 | * Not an egg -- overflow arithmetic. 16 and 32 bit. 4 | */ 5 | 6 | #ifndef _OVA_H 7 | #define _OVA_H 8 | 9 | int oadd16(unsigned char *pa, unsigned char *pb, unsigned char *pc); 10 | int oadd32(unsigned char *pa, unsigned char *pb, unsigned char *pc); 11 | int osub16(unsigned char *pa, unsigned char *pb, unsigned char *pc); 12 | int osub32(unsigned char *pa, unsigned char *pb, unsigned char *pc); 13 | int add16(unsigned char *pa, unsigned char *pb, unsigned char *pc); 14 | int add32(unsigned char *pa, unsigned char *pb, unsigned char *pc); 15 | int cm16(unsigned char *pa, unsigned char *pb); 16 | int cm32(unsigned char *pa, unsigned char *pb); 17 | int sub16(unsigned char *pa, unsigned char *pb, unsigned char *pc); 18 | int sub32(unsigned char *pa, unsigned char *pb, unsigned char *pc); 19 | int mull16(unsigned char *pa, unsigned char *pb, unsigned char *pc); 20 | int mulu16(unsigned char *pa, unsigned char *pb, unsigned char *pc); 21 | int div16(unsigned char *pa, unsigned char *pb, unsigned char *pc); 22 | int div32(unsigned char *pa, unsigned char *pb, unsigned char *pc); 23 | int mull32(unsigned char *pa, unsigned char *pb, unsigned char *pc); 24 | int mulu32(unsigned char *pa, unsigned char *pb, unsigned char *pc); 25 | 26 | #endif 27 | 28 | -------------------------------------------------------------------------------- /planet32.com: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ratboy666/am9511/ab8ed57f1a3eab09917f42f72f0ab70a74330501/planet32.com -------------------------------------------------------------------------------- /planeta.com: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ratboy666/am9511/ab8ed57f1a3eab09917f42f72f0ab70a74330501/planeta.com -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | /* test.c 2 | * 3 | * Test am9511 chip and emulator. 4 | */ 5 | 6 | #include 7 | #include 8 | 9 | #ifdef z80 10 | #include 11 | #endif 12 | 13 | #include "getopt.h" 14 | #include "am9511.h" 15 | #include "floatcnv.h" 16 | #include "types.h" 17 | 18 | 19 | /* Define fp_na() -- fp to native and 20 | * na_fp() -- native to fp 21 | */ 22 | #ifdef z80 23 | #define fp_na(x,y) fp_hi(x,y) 24 | #define na_fp(x,y) hi_fp(x,y) 25 | #else 26 | #define fp_na(x,y) fp_ie(x,y) 27 | #define na_fp(x,y) ie_fp(x,y) 28 | #endif 29 | 30 | 31 | #define NOTHING 32 | 33 | 34 | /* Poll am9511 and wait for not busy 35 | */ 36 | unsigned char am_wait(void *am9511) { 37 | int s; 38 | 39 | while ((s = am_status(am9511)) & AM_BUSY) 40 | NOTHING; 41 | return s; 42 | } 43 | 44 | /* am9511 test sequence 45 | * 46 | * am_test() also serves to show how to use the AM9511 device. 47 | * 48 | * I considered putting in a command interpreter, to feed sequences 49 | * from a file to the device (or emulator). But... I am going to 50 | * get the basic functions operational, and then write the "advanced" 51 | * script based test harness in MBASIC instead. 52 | */ 53 | 54 | void *fptmp; 55 | 56 | #ifdef TEST1 57 | 58 | /* TEST1 is NOP, data register push/pop, PUPI, CHSS, CHSD 59 | * 60 | * Note: we split test functions when they get too complex for the 61 | * optimizer (running under zxcc) 62 | */ 63 | 64 | void am_test1(void *am9511) { 65 | int s; 66 | int16 n; 67 | int32 nl; 68 | unsigned char v[4]; 69 | float x; 70 | 71 | printf("am_test1\n"); 72 | 73 | /* Basic test - execute a NOP 74 | */ 75 | am_wait(am9511); 76 | am_command(am9511, AM_NOP); 77 | s = am_wait(am9511); 78 | printf("NOP: am9511 status = %d\n", s); 79 | 80 | /* Push/pop 81 | * 82 | * Push low to high, pop high to low. 83 | */ 84 | am_push(am9511, 1); 85 | am_push(am9511, 2); 86 | am_push(am9511, 3); 87 | am_push(am9511, 4); 88 | 89 | 90 | if ((n = am_pop(am9511)) != 4) printf("push/pop error %d (4)\n", n); 91 | if ((n = am_pop(am9511)) != 3) printf("push/pop error %d (3)\n", n); 92 | if ((n = am_pop(am9511)) != 2) printf("push/pop error %d (2)\n", n); 93 | if ((n = am_pop(am9511)) != 1) printf("push/pop error %d (1)\n", n); 94 | 95 | /* Execute PUPI 96 | * 97 | * Pushes value of PI. Pop 4 bytes, convert from 98 | * am9511 to native float, and display value. 99 | */ 100 | am_command(am9511, AM_PUPI); 101 | s = am_wait(am9511); 102 | 103 | /* Demonstrate am_dump() 104 | */ 105 | am_dump(am9511, AM_FLOAT); /* AM_SINGLE, AM_DOUBLE, AM_FLOAT */ 106 | 107 | printf("PUPI: am9511 status = %d\n", s); 108 | v[3] = am_pop(am9511); 109 | v[2] = am_pop(am9511); 110 | v[1] = am_pop(am9511); 111 | v[0] = am_pop(am9511); 112 | 113 | /* Convert to native floating point 114 | */ 115 | am_fp(v, fptmp); 116 | fp_na(fptmp, &x); 117 | printf("PUPI: %g (should be 3.141592)\n", x); 118 | 119 | /* Execute CHSS. 120 | * 121 | * For 16 bit tests, we use int16. This is important for GCC 122 | * (not for z80 -- int is int16 on that platform). We just need 123 | * to be careful to use int16 and int32 as appropriate. 124 | */ 125 | n = 2; 126 | am_push(am9511, n); 127 | am_push(am9511, n >> 8); 128 | am_command(am9511, AM_CHS | AM_SINGLE); 129 | s = am_wait(am9511); 130 | printf("CHSS %d status = %d (%d)\n", n, s, AM_SIGN); 131 | n = am_pop(am9511); 132 | n = (n << 8) | am_pop(am9511); 133 | printf(" result -> %d\n", n); 134 | 135 | n = 0; 136 | am_push(am9511, n); 137 | am_push(am9511, n >> 8); 138 | am_command(am9511, AM_CHS | AM_SINGLE); 139 | s = am_wait(am9511); 140 | printf("CHSS %d status = %d (%d)\n", n, s, AM_ZERO); 141 | n = am_pop(am9511); 142 | n = (n << 8) | am_pop(am9511); 143 | printf(" result -> %d\n", n); 144 | 145 | n = -30; 146 | am_push(am9511, n); 147 | am_push(am9511, n >> 8); 148 | am_command(am9511, AM_CHS | AM_SINGLE); 149 | s = am_wait(am9511); 150 | printf("CHSS %d status = %d (%d)\n", n, s, 0); 151 | n = am_pop(am9511); 152 | n = (n << 8) | am_pop(am9511); 153 | printf(" result -> %d\n", n); 154 | 155 | n = 0x7fff; 156 | am_push(am9511, n); 157 | am_push(am9511, n >> 8); 158 | am_command(am9511, AM_CHS | AM_SINGLE); 159 | s = am_wait(am9511); 160 | printf("CHSS %d status = %d (%d)\n", n, s, AM_SIGN); 161 | n = am_pop(am9511); 162 | n = (n << 8) | am_pop(am9511); 163 | printf(" result -> %d\n", n); 164 | 165 | n = 0x8000; 166 | am_push(am9511, n); 167 | am_push(am9511, n >> 8); 168 | am_command(am9511, AM_CHS | AM_SINGLE); 169 | s = am_wait(am9511); 170 | printf("CHSS %d status = %d (%d)\n", n, s, AM_SIGN | AM_ERR_OVF); 171 | n = am_pop(am9511); 172 | n = (n << 8) | am_pop(am9511); 173 | printf(" result -> %d\n", n); 174 | 175 | /* Execute CHSD 176 | * 177 | * For CHSD tests, we cast to long. This is done, because we need 178 | * to use %ld format on z80, but int32 is not long on GCC. So, we 179 | * can either vary the format string, -or- cast the argument. On 180 | * the z80, this is the same and gives the correct result. On GCC, 181 | * this makes the argument match the format, and again produces 182 | * the desired result. 183 | */ 184 | 185 | nl = 2; 186 | am_push(am9511, nl); 187 | am_push(am9511, nl >> 8); 188 | am_push(am9511, nl >> 16); 189 | am_push(am9511, nl >> 24); 190 | am_command(am9511, AM_CHS | AM_DOUBLE); 191 | s = am_wait(am9511); 192 | printf("CHSD %ld status = %d (%d)\n", (long)nl, s, AM_SIGN); 193 | nl = am_pop(am9511); 194 | nl = (nl << 8) | am_pop(am9511); 195 | nl = (nl << 8) | am_pop(am9511); 196 | nl = (nl << 8) | am_pop(am9511); 197 | printf(" result -> %ld\n", (long)nl); 198 | } 199 | 200 | void am_test1a(void *am9511) { 201 | int s; 202 | int32 nl; 203 | 204 | nl = 0; 205 | am_push(am9511, nl); 206 | am_push(am9511, nl >> 8); 207 | am_push(am9511, nl >> 16); 208 | am_push(am9511, nl >> 24); 209 | am_command(am9511, AM_CHS | AM_DOUBLE); 210 | s = am_wait(am9511); 211 | printf("CHSD %ld status = %d (%d)\n", (long)nl, s, AM_ZERO); 212 | nl = am_pop(am9511); 213 | nl = (nl << 8) | am_pop(am9511); 214 | nl = (nl << 8) | am_pop(am9511); 215 | nl = (nl << 8) | am_pop(am9511); 216 | printf(" result -> %ld\n", (long)nl); 217 | 218 | nl = -30; 219 | am_push(am9511, nl); 220 | am_push(am9511, nl >> 8); 221 | am_push(am9511, nl >> 16); 222 | am_push(am9511, nl >> 24); 223 | am_command(am9511, AM_CHS | AM_DOUBLE); 224 | s = am_wait(am9511); 225 | printf("CHSD %ld status = %d (%d)\n", (long)nl, s, 0); 226 | nl = am_pop(am9511); 227 | nl = (nl << 8) | am_pop(am9511); 228 | nl = (nl << 8) | am_pop(am9511); 229 | nl = (nl << 8) | am_pop(am9511); 230 | printf(" result -> %ld\n", (long)nl); 231 | } 232 | 233 | #endif 234 | 235 | #ifdef TEST2 236 | 237 | /* TEST2: CHSD CHSF 238 | */ 239 | void am_test2(void *am9511) { 240 | int s; 241 | int32 nl; 242 | unsigned char v[4]; 243 | float x; 244 | 245 | printf("am_test2\n"); 246 | 247 | am_wait(am9511); 248 | 249 | nl = 0x7fffffff; 250 | am_push(am9511, 0xff); 251 | am_push(am9511, 0xff); 252 | am_push(am9511, 0xff); 253 | am_push(am9511, 0x7f); 254 | am_command(am9511, AM_CHS | AM_DOUBLE); 255 | s = am_wait(am9511); 256 | printf("CHSD %ld status = %d (%d)\n", (long)nl, s, AM_SIGN); 257 | nl = am_pop(am9511); 258 | nl = (nl << 8) | am_pop(am9511); 259 | nl = (nl << 8) | am_pop(am9511); 260 | nl = (nl << 8) | am_pop(am9511); 261 | printf(" result -> %ld\n", (long)nl); 262 | 263 | nl = 0x80000000; 264 | am_push(am9511, 0x00); 265 | am_push(am9511, 0x00); 266 | am_push(am9511, 0x00); 267 | am_push(am9511, 0x80); 268 | am_command(am9511, AM_CHS | AM_DOUBLE); 269 | s = am_wait(am9511); 270 | printf("CHSD %ld status = %d (%d)\n", (long)nl, s, AM_SIGN | AM_ERR_OVF); 271 | nl = am_pop(am9511); 272 | nl = (nl << 8) | am_pop(am9511); 273 | nl = (nl << 8) | am_pop(am9511); 274 | nl = (nl << 8) | am_pop(am9511); 275 | printf(" result -> %ld\n", (long)nl); 276 | 277 | /* Execute CHSF 278 | */ 279 | x = 3.2; 280 | na_fp(&x, fptmp); 281 | fp_am(fptmp, v); 282 | am_push(am9511, v[0]); 283 | am_push(am9511, v[1]); 284 | am_push(am9511, v[2]); 285 | am_push(am9511, v[3]); 286 | am_command(am9511, AM_CHSF); 287 | s = am_wait(am9511); 288 | printf("CHSF %g status = %d (%d)\n", x, s, AM_SIGN); 289 | v[3] = am_pop(am9511); 290 | v[2] = am_pop(am9511); 291 | v[1] = am_pop(am9511); 292 | v[0] = am_pop(am9511); 293 | am_fp(v, fptmp); 294 | fp_na(fptmp, &x); 295 | printf(" result -> %g\n", x); 296 | 297 | x = 0.0; 298 | na_fp(&x, fptmp); 299 | fp_am(fptmp, v); 300 | am_push(am9511, v[0]); 301 | am_push(am9511, v[1]); 302 | am_push(am9511, v[2]); 303 | am_push(am9511, v[3]); 304 | am_command(am9511, AM_CHSF); 305 | s = am_wait(am9511); 306 | printf("CHSF %g status = %d (%d)\n", x, s, AM_ZERO); 307 | v[3] = am_pop(am9511); 308 | v[2] = am_pop(am9511); 309 | v[1] = am_pop(am9511); 310 | v[0] = am_pop(am9511); 311 | am_fp(v, fptmp); 312 | fp_na(fptmp, &x); 313 | printf(" result -> %g\n", x); 314 | } 315 | 316 | #endif 317 | 318 | #ifdef TEST3 319 | 320 | /* TEST3: PTO, POP, XC, FIXS, FIXD 321 | */ 322 | void am_test3(void *am9511) { 323 | 324 | printf("am_test3\n"); 325 | 326 | am_wait(am9511); 327 | 328 | /* Execute PTO 329 | */ 330 | 331 | am_push(am9511, 1); 332 | am_push(am9511, 2); 333 | am_command(am9511, AM_PTO | AM_SINGLE); 334 | am_wait(am9511); 335 | am_command(am9511, AM_PTO | AM_DOUBLE); 336 | am_wait(am9511); 337 | am_command(am9511, AM_PTO | AM_FLOAT); 338 | am_wait(am9511); 339 | 340 | /* am_dump(am9511, AM_DOUBLE); */ 341 | 342 | /* Execute POP 343 | */ 344 | 345 | am_command(am9511, AM_POP | AM_FLOAT); 346 | am_wait(am9511); 347 | /* am_dump(am9511, AM_DOUBLE); */ 348 | 349 | /* Execute POP and XCH 350 | */ 351 | 352 | am_command(am9511, AM_POP | AM_DOUBLE); 353 | am_wait(am9511); 354 | /* am_dump(am9511, AM_DOUBLE); */ 355 | am_command(am9511, AM_XCH | AM_DOUBLE); 356 | am_wait(am9511); 357 | /* am_dump(am9511, AM_DOUBLE); */ 358 | 359 | /* FIXS and FIXD 360 | */ 361 | 362 | am_command(am9511, AM_PUPI); 363 | am_wait(am9511); 364 | am_command(am9511, AM_PTO | AM_FLOAT); 365 | am_wait(am9511); 366 | am_command(am9511, AM_FIXS); 367 | am_wait(am9511); 368 | /* am_dump(am9511, AM_SINGLE); */ 369 | printf("PUPI/FIXS ->\n"); 370 | printf(" %d\n", am_pop(am9511)); 371 | printf(" %d\n", am_pop(am9511)); 372 | 373 | am_command(am9511, AM_FIXD); 374 | am_wait(am9511); 375 | /* am_dump(am9511, AM_DOUBLE); */ 376 | printf("PUPI/FIXD ->\n"); 377 | printf(" %d\n", am_pop(am9511)); 378 | printf(" %d\n", am_pop(am9511)); 379 | printf(" %d\n", am_pop(am9511)); 380 | printf(" %d\n", am_pop(am9511)); 381 | 382 | /* Execute FLTD 383 | */ 384 | 385 | am_command(am9511, AM_FLTD); 386 | am_wait(am9511); 387 | /* am_dump(am9511, AM_FLOAT); */ 388 | 389 | /* Execute FLTS 390 | */ 391 | 392 | am_push(am9511, 1000 & 0xff); 393 | am_push(am9511, 1000 >> 8); 394 | am_command(am9511, AM_FLTS); 395 | am_wait(am9511); 396 | am_command(am9511, AM_FIXS); 397 | /* am_dump(am9511, AM_SINGLE); */ 398 | printf("1000/FLTS/FIXS ->\n"); 399 | printf(" %d\n", am_pop(am9511)); 400 | printf(" %d\n", am_pop(am9511)); 401 | } 402 | 403 | #endif 404 | 405 | 406 | #ifdef TEST4 407 | 408 | /* Basic arithmetic tests 409 | */ 410 | 411 | void am_test4(void *am9511) { 412 | int n, s, b; 413 | int32 nl; 414 | float x; 415 | unsigned char v[4]; 416 | 417 | printf("am_test4\n"); 418 | 419 | am_wait(am9511); 420 | 421 | /* Add: SADD DADD FADD 422 | */ 423 | 424 | /* SADD */ 425 | n = 1; 426 | am_push(am9511, n); 427 | am_push(am9511, n >> 8); 428 | n = 2; 429 | am_push(am9511, n); 430 | am_push(am9511, n >> 8); 431 | am_command(am9511, AM_ADD | AM_SINGLE); 432 | s = am_wait(am9511); 433 | n = am_pop(am9511); 434 | n = (n << 8) | am_pop(am9511); 435 | printf("SADD: 1 + 2 = %d status = %d\n", n, s); 436 | 437 | /* DADD */ 438 | nl = 1; 439 | am_push(am9511, nl); 440 | am_push(am9511, nl >> 8); 441 | am_push(am9511, nl >> 16); 442 | am_push(am9511, nl >> 24); 443 | nl = 2; 444 | am_push(am9511, nl); 445 | am_push(am9511, nl >> 8); 446 | am_push(am9511, nl >> 16); 447 | am_push(am9511, nl >> 24); 448 | am_command(am9511, AM_ADD | AM_DOUBLE); 449 | s = am_wait(am9511); 450 | nl = am_pop(am9511); 451 | b = am_pop(am9511); 452 | nl = nl << 8; 453 | nl = nl | b; 454 | b = am_pop(am9511); 455 | nl = nl << 8; 456 | nl = nl | b; 457 | b = am_pop(am9511); 458 | nl = nl << 8; 459 | nl = nl | b; 460 | printf("DADD: 1 + 2 = %ld status = %d\n", (long)nl, s); 461 | 462 | /* FADD */ 463 | n = 1; 464 | am_push(am9511, n); 465 | am_push(am9511, n >> 8); 466 | am_command(am9511, AM_FLTS); 467 | am_wait(am9511); 468 | n = 2; 469 | am_push(am9511, n); 470 | am_push(am9511, n >> 8); 471 | am_command(am9511, AM_FLTS); 472 | am_wait(am9511); 473 | am_command(am9511, AM_FADD); 474 | s = am_wait(am9511); 475 | v[3] = am_pop(am9511); 476 | v[2] = am_pop(am9511); 477 | v[1] = am_pop(am9511); 478 | v[0] = am_pop(am9511); 479 | am_fp(v, fptmp); 480 | fp_na(fptmp, &x); 481 | printf("FADD: 1.0 + 2.0 = %g status = %d\n", x, s); 482 | 483 | } 484 | 485 | #endif 486 | 487 | 488 | #ifdef TEST5 489 | 490 | void am_test5(void *am9511) { 491 | int s, b; 492 | int16 n; 493 | int32 nl; 494 | float x; 495 | unsigned char v[4]; 496 | 497 | printf("am_test5\n"); 498 | 499 | am_wait(am9511); 500 | 501 | /* Add: SSUB DSUB FSUB 502 | */ 503 | 504 | /* SSUB */ 505 | n = 1; 506 | am_push(am9511, n); 507 | am_push(am9511, n >> 8); 508 | n = 2; 509 | am_push(am9511, n); 510 | am_push(am9511, n >> 8); 511 | am_command(am9511, AM_SUB | AM_SINGLE); 512 | s = am_wait(am9511); 513 | n = am_pop(am9511); 514 | n = (n << 8) | am_pop(am9511); 515 | printf("SSUB: 1 - 2 = %d status = %d\n", n, s); 516 | 517 | /* DSUB */ 518 | nl = 1; 519 | am_push(am9511, nl); 520 | am_push(am9511, nl >> 8); 521 | am_push(am9511, nl >> 16); 522 | am_push(am9511, nl >> 24); 523 | nl = 2; 524 | am_push(am9511, nl); 525 | am_push(am9511, nl >> 8); 526 | am_push(am9511, nl >> 16); 527 | am_push(am9511, nl >> 24); 528 | am_command(am9511, AM_SUB | AM_DOUBLE); 529 | s = am_wait(am9511); 530 | nl = am_pop(am9511); 531 | b = am_pop(am9511); 532 | nl = nl << 8; 533 | nl = nl | b; 534 | b = am_pop(am9511); 535 | nl = nl << 8; 536 | nl = nl | b; 537 | b = am_pop(am9511); 538 | nl = nl << 8; 539 | nl = nl | b; 540 | printf("DSUB: 1 - 2 = %ld status = %d\n", (long)nl, s); 541 | 542 | /* FSUB */ 543 | n = 1; 544 | am_push(am9511, n); 545 | am_push(am9511, n >> 8); 546 | am_command(am9511, AM_FLTS); 547 | am_wait(am9511); 548 | n = 2; 549 | am_push(am9511, n); 550 | am_push(am9511, n >> 8); 551 | am_command(am9511, AM_FLTS); 552 | am_wait(am9511); 553 | am_command(am9511, AM_FSUB); 554 | am_wait(am9511); 555 | v[3] = am_pop(am9511); 556 | v[2] = am_pop(am9511); 557 | v[1] = am_pop(am9511); 558 | v[0] = am_pop(am9511); 559 | am_fp(v, fptmp); 560 | fp_na(fptmp, &x); 561 | printf("FSUB: 1.0 - 2.0 = %g status = %d\n", x, s); 562 | 563 | /* DIV: SDIV DDIV FDIV 564 | */ 565 | 566 | /* SDIV */ 567 | n = 10; 568 | am_push(am9511, n); 569 | am_push(am9511, n >> 8); 570 | n = 3; 571 | am_push(am9511, n); 572 | am_push(am9511, n >> 8); 573 | am_command(am9511, AM_DIV | AM_SINGLE); 574 | s = am_wait(am9511); 575 | n = am_pop(am9511); 576 | n = (n << 8) | am_pop(am9511); 577 | printf("SDIV: 10 / 3 = %d status = %d\n", n, s); 578 | 579 | /* DSUB */ 580 | nl = 10; 581 | am_push(am9511, nl); 582 | am_push(am9511, nl >> 8); 583 | am_push(am9511, nl >> 16); 584 | am_push(am9511, nl >> 24); 585 | nl = 3; 586 | am_push(am9511, nl); 587 | am_push(am9511, nl >> 8); 588 | am_push(am9511, nl >> 16); 589 | am_push(am9511, nl >> 24); 590 | am_command(am9511, AM_DIV | AM_DOUBLE); 591 | s = am_wait(am9511); 592 | nl = am_pop(am9511); 593 | b = am_pop(am9511); 594 | nl = nl << 8; 595 | nl = nl | b; 596 | b = am_pop(am9511); 597 | nl = nl << 8; 598 | nl = nl | b; 599 | b = am_pop(am9511); 600 | nl = nl << 8; 601 | nl = nl | b; 602 | printf("DDIV: 10 / 3 = %ld status = %d\n", (long)nl, s); 603 | 604 | /* FDIV */ 605 | n = 10; 606 | am_push(am9511, n); 607 | am_push(am9511, n >> 8); 608 | am_command(am9511, AM_FLTS); 609 | am_wait(am9511); 610 | n = 3; 611 | am_push(am9511, n); 612 | am_push(am9511, n >> 8); 613 | am_command(am9511, AM_FLTS); 614 | am_wait(am9511); 615 | am_command(am9511, AM_FDIV); 616 | s = am_wait(am9511); 617 | v[3] = am_pop(am9511); 618 | v[2] = am_pop(am9511); 619 | v[1] = am_pop(am9511); 620 | v[0] = am_pop(am9511); 621 | am_fp(v, fptmp); 622 | fp_na(fptmp, &x); 623 | printf("FDIV: 10.0 / 3.0 = %g status = %d\n", x, s); 624 | } 625 | 626 | #endif 627 | 628 | 629 | #ifdef TEST6 630 | 631 | /* multiply, float, single lower/upper, double lower/upper 632 | */ 633 | 634 | void am_test6(void *am9511) { 635 | int s, b; 636 | int16 n; 637 | int32 nl; 638 | float x; 639 | unsigned char v[4]; 640 | 641 | printf("am_test6\n"); 642 | 643 | am_wait(am9511); 644 | 645 | /* MUL: SMUL DMUL FMUL 646 | */ 647 | 648 | /* SMUL */ 649 | n = 3; 650 | am_push(am9511, n); 651 | am_push(am9511, n >> 8); 652 | n = 10; 653 | am_push(am9511, n); 654 | am_push(am9511, n >> 8); 655 | am_command(am9511, AM_MUL | AM_SINGLE); 656 | s = am_wait(am9511); 657 | n = am_pop(am9511); 658 | n = (n << 8) | am_pop(am9511); 659 | printf("SMUL: 10 * 3 = %d status = %d\n", n, s); 660 | 661 | /* DMUL*/ 662 | nl = 10; 663 | am_push(am9511, nl); 664 | am_push(am9511, nl >> 8); 665 | am_push(am9511, nl >> 16); 666 | am_push(am9511, nl >> 24); 667 | nl = 3; 668 | am_push(am9511, nl); 669 | am_push(am9511, nl >> 8); 670 | am_push(am9511, nl >> 16); 671 | am_push(am9511, nl >> 24); 672 | am_command(am9511, AM_MUL | AM_DOUBLE); 673 | s = am_wait(am9511); 674 | nl = am_pop(am9511); 675 | b = am_pop(am9511); 676 | nl = nl << 8; 677 | nl = nl | b; 678 | b = am_pop(am9511); 679 | nl = nl << 8; 680 | nl = nl | b; 681 | b = am_pop(am9511); 682 | nl = nl << 8; 683 | nl = nl | b; 684 | printf("DMUL: 10 * 3 = %ld status = %d\n", (long)nl, s); 685 | 686 | /* FMUL */ 687 | n = 10; 688 | am_push(am9511, n); 689 | am_push(am9511, n >> 8); 690 | am_command(am9511, AM_FLTS); 691 | am_wait(am9511); 692 | n = 3; 693 | am_push(am9511, n); 694 | am_push(am9511, n >> 8); 695 | am_command(am9511, AM_FLTS); 696 | am_wait(am9511); 697 | am_command(am9511, AM_FMUL); 698 | am_wait(am9511); 699 | v[3] = am_pop(am9511); 700 | v[2] = am_pop(am9511); 701 | v[1] = am_pop(am9511); 702 | v[0] = am_pop(am9511); 703 | am_fp(v, fptmp); 704 | fp_na(fptmp, &x); 705 | printf("FMUL: 10.0 * 3.0 = %g status = %d\n", x, s); 706 | 707 | /* MUU */ 708 | 709 | /* SMUU */ 710 | printf("0x1000 * 0x40 = 0x0004 0000\n"); 711 | n = 0x1000; 712 | am_push(am9511, n); 713 | am_push(am9511, n >> 8); 714 | n = 0x40; 715 | am_push(am9511, n); 716 | am_push(am9511, n >> 8); 717 | /* 0x1000 * 0x40 = 0x40 0000 */ 718 | am_command(am9511, AM_MUL | AM_SINGLE); 719 | s = am_wait(am9511); 720 | n = am_pop(am9511); 721 | n = (n << 8) | am_pop(am9511); 722 | printf("SMUL: 0x1000 * 0x40 = %d (0) status = %d (34)\n", n, s); 723 | 724 | n = 0x1000; 725 | am_push(am9511, n); 726 | am_push(am9511, n >> 8); 727 | n = 0x40; 728 | am_push(am9511, n); 729 | am_push(am9511, n >> 8); 730 | /* 0x1000 * 0x40 = 0x40 0000 */ 731 | am_command(am9511, AM_MUU | AM_SINGLE); 732 | s = am_wait(am9511); 733 | n = am_pop(am9511); 734 | n = (n << 8) | am_pop(am9511); 735 | printf("SMUU: 0x1000 * 0x40 = %d (4) status = %d (0)\n", n, s); 736 | } 737 | 738 | #endif 739 | 740 | #ifdef TEST7 741 | 742 | /* Signed multiply SMUL SMUU DMUL DMUD 743 | */ 744 | void am_test7(void *am9511) { 745 | int s, b; 746 | int16 n; 747 | int32 nl; 748 | 749 | printf("am_test7\n"); 750 | 751 | am_wait(am9511); 752 | 753 | /* SMUL 10 * 3 result in am_test6() 754 | */ 755 | n = 3; 756 | am_push(am9511, n); 757 | am_push(am9511, n >> 8); 758 | n = 10; 759 | am_push(am9511, n); 760 | am_push(am9511, n >> 8); 761 | am_command(am9511, AM_MUU | AM_SINGLE); 762 | s = am_wait(am9511); 763 | n = am_pop(am9511); 764 | n = (n << 8) | am_pop(am9511); 765 | printf("SMUU: 10 * 3 = %d (0) status = %d (32)\n", n, s); 766 | 767 | n = -3; 768 | am_push(am9511, n); 769 | am_push(am9511, n >> 8); 770 | n = -10; 771 | am_push(am9511, n); 772 | am_push(am9511, n >> 8); 773 | am_command(am9511, AM_MUL | AM_SINGLE); 774 | s = am_wait(am9511); 775 | n = am_pop(am9511); 776 | n = (n << 8) | am_pop(am9511); 777 | printf("SMUL: -10 * -3 = %d (30) status = %d (0)\n", n, s); 778 | 779 | n = -3; 780 | am_push(am9511, n); 781 | am_push(am9511, n >> 8); 782 | n = -10; 783 | am_push(am9511, n); 784 | am_push(am9511, n >> 8); 785 | am_command(am9511, AM_MUU | AM_SINGLE); 786 | s = am_wait(am9511); 787 | n = am_pop(am9511); 788 | n = (n << 8) | am_pop(am9511); 789 | printf("SMUU: -10 * -3 = %d (0) status = %d (32)\n", n, s); 790 | 791 | n = -3; 792 | am_push(am9511, n); 793 | am_push(am9511, n >> 8); 794 | n = 10; 795 | am_push(am9511, n); 796 | am_push(am9511, n >> 8); 797 | am_command(am9511, AM_MUL | AM_SINGLE); 798 | s = am_wait(am9511); 799 | n = am_pop(am9511); 800 | n = (n << 8) | am_pop(am9511); 801 | printf("SMUL: 10 * -3 = %d (-30) status = %d (64)\n", n, s); 802 | 803 | n = -3; 804 | am_push(am9511, n); 805 | am_push(am9511, n >> 8); 806 | n = 10; 807 | am_push(am9511, n); 808 | am_push(am9511, n >> 8); 809 | am_command(am9511, AM_MUU | AM_SINGLE); 810 | s = am_wait(am9511); 811 | n = am_pop(am9511); 812 | n = (n << 8) | am_pop(am9511); 813 | printf("SMUU: 10 * -3 = %d (-1) status = %d (64)\n", n, s); 814 | 815 | /* Test DMUL / DMUU */ 816 | 817 | nl = 3; 818 | am_push(am9511, nl); 819 | am_push(am9511, nl >> 8); 820 | am_push(am9511, nl >> 16); 821 | am_push(am9511, nl >> 24); 822 | nl = 10; 823 | am_push(am9511, nl); 824 | am_push(am9511, nl >> 8); 825 | am_push(am9511, nl >> 16); 826 | am_push(am9511, nl >> 24); 827 | am_command(am9511, AM_MUU | AM_DOUBLE); 828 | s = am_wait(am9511); 829 | nl = am_pop(am9511); 830 | b = am_pop(am9511); 831 | nl = nl << 8; 832 | nl = nl | b; 833 | b = am_pop(am9511); 834 | nl = nl << 8; 835 | nl = nl | b; 836 | b = am_pop(am9511); 837 | nl = nl << 8; 838 | nl = nl | b; 839 | printf("DMUU: 10 * 3 = %ld (0) status = %d (32)\n", (long)nl, s); 840 | 841 | nl = -3; 842 | am_push(am9511, nl); 843 | am_push(am9511, nl >> 8); 844 | am_push(am9511, nl >> 16); 845 | am_push(am9511, nl >> 24); 846 | nl = -10; 847 | am_push(am9511, nl); 848 | am_push(am9511, nl >> 8); 849 | am_push(am9511, nl >> 16); 850 | am_push(am9511, nl >> 24); 851 | am_command(am9511, AM_MUL | AM_DOUBLE); 852 | s = am_wait(am9511); 853 | nl = am_pop(am9511); 854 | b = am_pop(am9511); 855 | nl = nl << 8; 856 | nl = nl | b; 857 | b = am_pop(am9511); 858 | nl = nl << 8; 859 | nl = nl | b; 860 | b = am_pop(am9511); 861 | nl = nl << 8; 862 | nl = nl | b; 863 | printf("DMUL: -10 * -3 = %ld (30) status = %d (32)\n", (long)nl, s); 864 | 865 | } 866 | 867 | #endif 868 | 869 | #ifdef TEST8 870 | 871 | void am_test8(void *am9511) { 872 | printf("am_test8\n"); 873 | 874 | am_wait(am9511); 875 | } 876 | 877 | #endif 878 | 879 | #ifdef TEST9 880 | 881 | void am_test9(void *am9511) { 882 | int s, b; 883 | int32 nl; 884 | 885 | printf("am_test9\n"); 886 | 887 | am_wait(am9511); 888 | 889 | nl = -3; 890 | am_push(am9511, nl); 891 | am_push(am9511, nl >> 8); 892 | am_push(am9511, nl >> 16); 893 | am_push(am9511, nl >> 24); 894 | nl = -10; 895 | am_push(am9511, nl); 896 | am_push(am9511, nl >> 8); 897 | am_push(am9511, nl >> 16); 898 | am_push(am9511, nl >> 24); 899 | am_command(am9511, AM_MUU | AM_DOUBLE); 900 | s = am_wait(am9511); 901 | nl = am_pop(am9511); 902 | b = am_pop(am9511); 903 | nl = nl << 8; 904 | nl = nl | b; 905 | b = am_pop(am9511); 906 | nl = nl << 8; 907 | nl = nl | b; 908 | b = am_pop(am9511); 909 | nl = nl << 8; 910 | nl = nl | b; 911 | printf("DMUU: -10 * -3 = %ld (0) status = %d (32)\n", (long)nl, s); 912 | 913 | nl = -3; 914 | am_push(am9511, nl); 915 | am_push(am9511, nl >> 8); 916 | am_push(am9511, nl >> 16); 917 | am_push(am9511, nl >> 24); 918 | nl = 10; 919 | am_push(am9511, nl); 920 | am_push(am9511, nl >> 8); 921 | am_push(am9511, nl >> 16); 922 | am_push(am9511, nl >> 24); 923 | am_command(am9511, AM_MUL | AM_DOUBLE); 924 | s = am_wait(am9511); 925 | nl = am_pop(am9511); 926 | b = am_pop(am9511); 927 | nl = nl << 8; 928 | nl = nl | b; 929 | b = am_pop(am9511); 930 | nl = nl << 8; 931 | nl = nl | b; 932 | b = am_pop(am9511); 933 | nl = nl << 8; 934 | nl = nl | b; 935 | printf("DMUL: 10 * -3 = %ld (-30) status = %d (64)\n", (long)nl, s); 936 | 937 | nl = -3; 938 | am_push(am9511, nl); 939 | am_push(am9511, nl >> 8); 940 | am_push(am9511, nl >> 16); 941 | am_push(am9511, nl >> 24); 942 | nl = 10; 943 | am_push(am9511, nl); 944 | am_push(am9511, nl >> 8); 945 | am_push(am9511, nl >> 16); 946 | am_push(am9511, nl >> 24); 947 | am_command(am9511, AM_MUU | AM_DOUBLE); 948 | s = am_wait(am9511); 949 | nl = am_pop(am9511); 950 | b = am_pop(am9511); 951 | nl = nl << 8; 952 | nl = nl | b; 953 | b = am_pop(am9511); 954 | nl = nl << 8; 955 | nl = nl | b; 956 | b = am_pop(am9511); 957 | nl = nl << 8; 958 | nl = nl | b; 959 | printf("DMUU: 10 * -3 = %ld (-1) status = %d (64)\n", (long)nl, s); 960 | } 961 | 962 | #endif 963 | 964 | #ifdef TEST10 965 | 966 | void am_test10(void *am9511) { 967 | printf("am_test10\n"); 968 | 969 | am_wait(am9511); 970 | } 971 | 972 | #endif 973 | 974 | #ifdef TEST11 975 | 976 | void am_test11(void *am9511) { 977 | printf("am_test11\n"); 978 | 979 | am_wait(am9511); 980 | } 981 | 982 | #endif 983 | 984 | #ifdef TEST12 985 | 986 | void am_test12(void *am9511) { 987 | printf("am_test12\n"); 988 | 989 | am_wait(am9511); 990 | } 991 | 992 | #endif 993 | 994 | void am_test(void *am9511) { 995 | #ifdef TEST1 996 | am_test1(am9511); 997 | am_test1a(am9511); 998 | #endif 999 | #ifdef TEST2 1000 | am_test2(am9511); 1001 | #endif 1002 | #ifdef TEST3 1003 | am_test3(am9511); 1004 | #endif 1005 | #ifdef TEST4 1006 | am_test4(am9511); 1007 | #endif 1008 | #ifdef TEST5 1009 | am_test5(am9511); 1010 | #endif 1011 | #ifdef TEST6 1012 | am_test6(am9511); 1013 | #endif 1014 | #ifdef TEST7 1015 | am_test7(am9511); 1016 | #endif 1017 | #ifdef TEST8 1018 | am_test8(am9511); 1019 | #endif 1020 | #ifdef TEST9 1021 | am_test9(am9511); 1022 | #endif 1023 | #ifdef TEST10 1024 | am_test10(am9511); 1025 | #endif 1026 | #ifdef TEST11 1027 | am_test11(am9511); 1028 | #endif 1029 | #ifdef TEST12 1030 | am_test12(am9511); 1031 | #endif 1032 | } 1033 | 1034 | 1035 | /* Give usage for am9511 test 1036 | */ 1037 | void usage(char *p) { 1038 | printf("usage: %s [-d port] [-s port]\n", p); 1039 | printf(" -d port set data port\n"); 1040 | printf(" -s port set status port\n"); 1041 | printf("\n"); 1042 | printf("port numbers are specified in decimal\n"); 1043 | exit(1); 1044 | } 1045 | 1046 | 1047 | int main(int ac, char **av) { 1048 | int ch, s, d; 1049 | void *am9511; 1050 | 1051 | #ifdef z80 1052 | /* Expand arguments for HI-TECH C. 1053 | */ 1054 | av = _getargs((char *)0x81, "am9511"); 1055 | ac = _argc_; 1056 | #endif 1057 | 1058 | printf("test am9511: %s\n", av[0]); 1059 | 1060 | s = -1; 1061 | d = -1; 1062 | while ((ch = getopt(ac, av, "S:D:s:d:")) != EOF) 1063 | switch (ch) { 1064 | case 's': 1065 | case 'S': 1066 | s = atoi(optarg); 1067 | break; 1068 | case 'd': 1069 | case 'D': 1070 | d = atoi(optarg); 1071 | break; 1072 | case '?': 1073 | default: 1074 | usage(av[0]); 1075 | } 1076 | ac -= optind; 1077 | av += optind; 1078 | 1079 | fptmp = malloc(fp_size()); 1080 | if (fptmp == NULL) { 1081 | printf("cannot malloc fptmp\n"); 1082 | return 1; 1083 | } 1084 | 1085 | /* Create AM9511 1086 | */ 1087 | am9511 = am_create(s, d); 1088 | if (am9511 == NULL) { 1089 | fprintf(stderr, "Cannot create\n"); 1090 | return 1; 1091 | } 1092 | 1093 | /* Reset AM9511. If using actual hardware, passes in status and 1094 | * data ports that will be used. 1095 | */ 1096 | am_reset(am9511); 1097 | 1098 | am_test(am9511); 1099 | 1100 | return 0; 1101 | } 1102 | -------------------------------------------------------------------------------- /test.com: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ratboy666/am9511/ab8ed57f1a3eab09917f42f72f0ab70a74330501/test.com -------------------------------------------------------------------------------- /test14.com: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ratboy666/am9511/ab8ed57f1a3eab09917f42f72f0ab70a74330501/test14.com -------------------------------------------------------------------------------- /test58.com: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ratboy666/am9511/ab8ed57f1a3eab09917f42f72f0ab70a74330501/test58.com -------------------------------------------------------------------------------- /test912.com: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ratboy666/am9511/ab8ed57f1a3eab09917f42f72f0ab70a74330501/test912.com -------------------------------------------------------------------------------- /testhw.com: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ratboy666/am9511/ab8ed57f1a3eab09917f42f72f0ab70a74330501/testhw.com -------------------------------------------------------------------------------- /testhw14.com: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ratboy666/am9511/ab8ed57f1a3eab09917f42f72f0ab70a74330501/testhw14.com -------------------------------------------------------------------------------- /testhw58.com: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ratboy666/am9511/ab8ed57f1a3eab09917f42f72f0ab70a74330501/testhw58.com -------------------------------------------------------------------------------- /testhw912.com: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ratboy666/am9511/ab8ed57f1a3eab09917f42f72f0ab70a74330501/testhw912.com -------------------------------------------------------------------------------- /types.h: -------------------------------------------------------------------------------- 1 | /* types.h 2 | */ 3 | 4 | 5 | #ifndef TYPES_H 6 | #define TYPES_H 7 | 8 | #ifdef z80 9 | 10 | #include 11 | 12 | /* Types for Hi-Tech C, z80, 8 bit 13 | */ 14 | #define uint8 unsigned char 15 | #define uint16 unsigned int 16 | #define uint32 unsigned long 17 | #define int8 signed char 18 | #define int16 int 19 | #define int32 long 20 | 21 | #else 22 | 23 | /* Types for GCC/TCC, Linux, 64 bit 24 | */ 25 | #include 26 | 27 | #define uint8 uint8_t 28 | #define uint16 uint16_t 29 | #define uint32 uint32_t 30 | #define int8 int8_t 31 | #define int16 int16_t 32 | #define int32 int32_t 33 | 34 | #endif 35 | 36 | #endif 37 | --------------------------------------------------------------------------------