1
;---------------------------------------------------------------------------------------------------------
2
; Source code converted by MSXBAS2ASM - MSX BASIC TO Z80 ASSEMBLY CONVERTER
3
; MSXBAS2ASM developed by Amaury Carvalho, 2019, Brazil
4
; http://launchpad.net/msxbas2asm
5
;---------------------------------------------------------------------------------------------------------
7
;--------------------------------------------------------
8
; MSX BIOS DATA/FUNCTION POINTERS
9
;--------------------------------------------------------
11
;---------------------------------------------------------------------------------------------------------
13
;---------------------------------------------------------------------------------------------------------
15
BIOS_CALBAS: equ 0x0159
16
BIOS_OUTDO: equ 0x0018 ; output to current device (i.e. screen)
17
BIOS_CHPUT: equ 0x00A2
19
BIOS_POSIT: equ 0x00C6
21
BIOS_CHGET: equ 0x009F
22
BIOS_CHSNS: equ 0x009C
23
BIOS_INLIN: equ 0x00B1
24
BIOS_PINLIN: equ 0x00AE
25
BIOS_QINLIN: equ 0x00B4
26
BIOS_GTSTCK: equ 0x00D5
27
BIOS_GTTRIG: equ 0x00D8
28
BIOS_GTPAD: equ 0x00DB
29
BIOS_GTPDL: equ 0x00DE
30
BIOS_DISSCR: equ 0x0041
31
BIOS_ENASCR: equ 0x0044
32
BIOS_CHGMOD: equ 0x005F
33
BIOS_CHGCLR: equ 0x0062
34
BIOS_CLRSPR: equ 0x0069
35
BIOS_INITXT: equ 0x006C ; init text mode 40 columns
36
BIOS_INIT32: equ 0x006F ; init text mode 32 columns
37
BIOS_INIGRP: equ 0x0072
38
BIOS_INIMLT: equ 0x0075
39
BIOS_SETTXT: equ 0x0078 ; set text mode 40 columns
40
BIOS_SETT32: equ 0x007B ; set text mode 32 columns
41
BIOS_SETGRP: equ 0x007E
42
BIOS_SETMLT: equ 0x0081
43
BIOS_CALPAT: equ 0x0084
44
BIOS_CALATR: equ 0x0087
45
BIOS_GSPSIZ: equ 0x008A
46
BIOS_GRPPRT: equ 0x008D
47
BIOS_ERAFNK: equ 0x00CC
48
BIOS_DSPFNK: equ 0x00CF
49
BIOS_TOTEXT: equ 0x00D2
50
BIOS_BREAKX: equ 0x00B7
51
BIOS_ISCNTC: equ 0x03FB
52
BIOS_CHKRAM: equ 0x0000
53
BIOS_GICINI: equ 0x0090
54
BIOS_WRTPSG: equ 0x0093
55
BIOS_REDPSG: equ 0x0096
56
BIOS_STRTMS: equ 0x0099
57
BIOS_KEYINT: equ 0x0038
58
BIOS_CALSLT: equ 0x001C
59
BIOS_ENASLT: equ 0x0024
60
BIOS_RSLREG: equ 0x0138
61
BIOS_SCALXY: equ 0x010E
62
BIOS_MAPXYC: equ 0x0111 ; in BC = X, DE = Y
63
BIOS_READC: equ 0x011D ; out A = color of current pixel
64
BIOS_SETATR: equ 0x011A ; in A = color code
65
BIOS_SETC: equ 0x0120 ; set current pixel to color from SETATR
66
BIOS_NSETCX: equ 0x0123 ; in HL = pixel fill count
67
BIOS_SCANR: equ 0x012C ; in B=Fill switch, DE=Skip count, out DE=Skip remainder, HL=Pixel count
68
BIOS_SCANL: equ 0x012F ; out HL=Pixel count
69
BIOS_FETCHC: equ 0x0114 ; out A = cursor mask, HL = VRAM address of cursor
70
BIOS_STOREC: equ 0x0117 ; in A = cursor mask, HL = VRAM address of cursor
71
BIOS_RESET: equ 0x7D17 ; restart BASIC
72
BIOS_IOALLOC: equ 0X7e6b ; memory setup
74
BIOS_GETVCP: equ 0x0150 ; get PSG voice buffer address (in A = voice number, out HL = address of byte 2)
75
BIOS_GETVC2: equ 0x0153 ; get PSG voice buffer address (VOICEN = voice number, in L = byte number 0-36, out HL = address)
77
BIOS_CHPUT_LF: equ 0x0908
78
BIOS_CHPUT_CR: equ 0x0A81
79
BIOS_CHPUT_TAB: equ 0x0A71
82
BIOS_CHKNEW: equ 0x0165 ; C-flag set if screenmode = 5, 6, 7 or 8
83
BIOS_EXTROM: equ 0x015F
84
BIOS_SCALXY2: equ 0x008D ; in BC = X, DE = Y
85
BIOS_MAPXYC2: equ 0x0091 ; in BC = X, DE = Y
86
BIOS_SETC2: equ 0x009D ; set current pixel to color from SETATR
87
BIOS_READC2: equ 0x0095 ; out A = color of current pixel
88
BIOS_CHGMOD2: equ 0x00D1 ; in A = screenmode
89
BIOS_DOBOXF: equ 0x0079 ; hl = basic text pointer
90
BIOS_GRPPRT2: equ 0x0089 ; a = character
91
BIOS_CHGCLR2: equ 0x0111 ; change color, a = screen mode
92
BIOS_CALPAT2: equ 0x00F9
93
BIOS_CALATR2: equ 0x00FD
94
BIOS_GSPSIZ2: equ 0x0101
95
BIOS_CLRSPR2: equ 0x00F5
97
;---------------------------------------------------------------------------------------------------------
99
;---------------------------------------------------------------------------------------------------------
101
BIOS_VERSION: equ 0x002D ; 0 = MSX1, 1 = MSX2, 2 = MSX2+, 3 = MSXturboR
102
BIOS_FORCLR: equ 0xF3E9
103
BIOS_BAKCLR: equ 0xF3EA
104
BIOS_BDRCLR: equ 0xF3EB
105
BIOS_ATRBYT: equ 0xF3F2
106
BIOS_INTFLG: equ 0xFC9B
107
BIOS_EXPTBL: equ 0xFCC1
108
BIOS_JIFFY: equ 0xFC9E
109
BIOS_BOTTOM: equ 0xFC48
110
BIOS_HIMEM: equ 0xFC4A
111
BIOS_SCRMOD: equ 0xFCAF ; 0=40x24 Text Mode, 1=32x24 Text Mode, 2=Graphics Mode, 3=Multicolour Mode.
112
BIOS_CLIKSW: equ 0xF3DB ; 0=keyboard click off, 1=keyboard click on
113
BIOS_GRPACX: equ 0xFCB7
114
BIOS_GRPACY: equ 0xFCB9
115
BIOS_DATLIN: equ 0xF6A3 ; 2 - line number of DATA statement read by READ statement
116
BIOS_DATPTR: equ 0xF6C8 ; 2 - address of data read by executing READ statement
117
BIOS_FLGINP: equ 0xF6A6 ; 1 - flag used in INPUT or READ
118
BIOS_TEMP: equ 0xF6A7 ; 2
119
BIOS_TEMP2: equ 0xF6BC ; 2
120
BIOS_TEMP3: equ 0xF69D ; 2
121
BIOS_TEMP8: equ 0xF69F ; 2
122
BIOS_TEMP9: equ 0xF7B8 ; 2
123
BIOS_OLDSCR: equ 0xFCB0 ; screen mode of the last text mode set
124
BIOS_LINL40: equ 0xF3AE ; width for 40 columns screen mode
125
BIOS_LINL32: equ 0xF3AF ; width for 32 columns screen mode
126
BIOS_LINLEN: equ 0xF3B0 ; current width for text screen mode
127
BIOS_CLMLST: equ 0xF3B2 ; minimum number of columns that must still be available on a line for a CRLF
128
BIOS_TXTNAM: equ 0xF3B3 ; characters table name
130
BIOS_VOICEN: equ 0xFB38 ; PSG voice number
131
BIOS_MCLTAB: equ 0xF956
132
BIOS_PRSCNT: equ 0xFB35
133
BIOS_SAVSP: equ 0xFB36
134
BIOS_QUEUEN: equ 0xFB3E
135
BIOS_MUSICF: equ 0xFB3F ;contains 3 bit flags set by the STRTMS. Bits 0, 1 and 2 correspond to VOICAQ, VOICBQ and VOICCQ.
136
BIOS_PLYCNT: equ 0xFB40
138
BIOS_DRWFLG: equ 0xFCBB
139
BIOS_MCLFLG: equ 0xF958
141
BIOS_SLTROM: equ 0xFCC1
142
BIOS_RAMAD0: equ 0xF341 ; Main-RAM Slot (00000h~03FFFh)
143
BIOS_RAMAD1: equ 0xF342 ; Main-RAM Slot (04000h~07FFFh)
144
BIOS_RAMAD2: equ 0xF343 ; Main-RAM Slot (08000h~0BFFFh)
145
BIOS_RAMAD3: equ 0xF344 ; Main-RAM Slot (0C000h~0FFFFh)
149
;--------------------------------------------------------
150
; MSX BASIC DATA/FUNCTION POINTERS
151
;--------------------------------------------------------
153
;---------------------------------------------------------------------------------------------------------
154
; MSX BASIC FUNCTIONS
155
;---------------------------------------------------------------------------------------------------------
157
BASIC_AUTO: equ 0x3973
158
BASIC_AND: equ 0x3A18
159
BASIC_ATTR: equ 0x39FE
160
BASIC_BASE: equ 0x39BE
161
BASIC_BSAVE: equ 0x39CC
162
BASIC_BLOAD: equ 0x39CA
163
BASIC_BEEP: equ 0x39AC
164
BASIC_CALL: equ 0x39C0
165
BASIC_CLOSE: equ 0x3994
166
BASIC_COPY: equ 0x39D8
167
BASIC_CONT: equ 0x395E
168
BASIC_CLEAR: equ 0x3950
169
BASIC_CLOAD: equ 0x3962
170
BASIC_CSAVE: equ 0x3960
171
BASIC_CSRLIN: equ 0x39FC
172
BASIC_CIRCLE: equ 0x39A4
173
BASIC_COLOR: equ 0x39A6
174
BASIC_CLS: equ 0x396A
175
BASIC_CMD: equ 0x39DA
176
BASIC_DELETE: equ 0x397C
177
BASIC_DATA: equ 0x3934
178
BASIC_DIM: equ 0x3938
179
BASIC_DEFSTR: equ 0x3982
180
BASIC_DEFINT: equ 0x3984
181
BASIC_DEFSNG: equ 0x3986
182
BASIC_DEFDBL: equ 0x3988
183
BASIC_DSKO: equ 0x39CE
184
BASIC_DEF: equ 0x395A
185
BASIC_DSKI: equ 0x3A00
186
BASIC_DRAW: equ 0x39A8
187
BASIC_ELSE: equ 0x396E
188
BASIC_END: equ 0x392E
189
BASIC_ERASE: equ 0x3976
190
BASIC_ERROR: equ 0x3978
191
BASIC_ERL: equ 0x39EE
192
BASIC_ERR: equ 0x39F0
193
BASIC_EQU: equ 0x3A1E
194
BASIC_FOR: equ 0x3920
195
BASIC_FIELD: equ 0x398E
196
BASIC_FILES: equ 0x39AA
198
BASIC_GOTO: equ 0x393E
199
BASIC_GOSUB: equ 0x3948
200
BASIC_GET: equ 0x3990
201
BASIC_INPUT: equ 0x3936
203
BASIC_INSTR: equ 0x39F6
204
BASIC_IMP: equ 0x3A20
205
BASIC_INKEY: equ 0x3A04
206
BASIC_IPL: equ 0x39D6
207
BASIC_KILL: equ 0x39D4
208
BASIC_KEY: equ 0x3964
209
BASIC_LPRINT: equ 0x394C
210
BASIC_LLIST: equ 0x3968
211
BASIC_LET: equ 0x393C
212
BASIC_LOCATE: equ 0x39DC
213
BASIC_LINE: equ 0x398A
214
BASIC_LOAD: equ 0x3996
215
BASIC_LSET: equ 0x399C
216
BASIC_LIST: equ 0x3952
217
BASIC_LFILES: equ 0x39A2
218
BASIC_MOTOR: equ 0x39C8
219
BASIC_MERGE: equ 0x3998
220
BASIC_MOD: equ 0x3A22
221
BASIC_MAX: equ 0x39C6
222
BASIC_NEXT: equ 0x3932
223
BASIC_NAME: equ 0x39D2
224
BASIC_NEW: equ 0x3954
225
BASIC_NOT: equ 0x39EC
226
BASIC_OPEN: equ 0x398C
227
BASIC_OUT: equ 0x3964
230
BASIC_OFF: equ 0x3A02
231
BASIC_PRINT: equ 0x394E
232
BASIC_PUT: equ 0x3992
233
BASIC_POKE: equ 0x395C
234
BASIC_PSET: equ 0x39B0
235
BASIC_PRESET: equ 0x39B2
236
BASIC_POINT: equ 0x3A06
237
BASIC_PAINT: equ 0x39AA
238
BASIC_PLAY: equ 0x39AE
239
BASIC_RETURN: equ 0x3948
240
BASIC_READ: equ 0x393A
241
BASIC_RUN: equ 0x3940
242
BASIC_RESTORE:equ 0x3944
243
BASIC_REM: equ 0x394A
244
BASIC_RESUME: equ 0x397A
245
BASIC_RSET: equ 0x399E
246
BASIC_RENUM: equ 0x3980
247
BASIC_SCREEN: equ 0x39B6
248
BASIC_SPRITE: equ 0x39BA
249
BASIC_STOP: equ 0x394C
250
BASIC_SWAP: equ 0x3974
251
BASIC_SET: equ 0x39D0
252
BASIC_SAVE: equ 0x39A0
253
BASIC_SPC: equ 0x39EA
254
BASIC_STEP: equ 0x39E4
255
BASIC_STRING: equ 0x39F2
256
BASIC_SPACE1: equ 0x397E
257
BASIC_SOUND: equ 0x39B4
258
BASIC_THEN: equ 0x39E0
259
BASIC_TRON: equ 0x3970
260
BASIC_TROFF: equ 0x3972
261
BASIC_TAB: equ 0x39E2
263
BASIC_TIME: equ 0x39C2
264
BASIC_USING: equ 0x39F4
265
BASIC_USR: equ 0x39E6
266
BASIC_VARPTR: equ 0x39FA
267
BASIC_VDP: equ 0x39BC
268
BASIC_VPOKE: equ 0x39B8
269
BASIC_WIDTH: equ 0x396C
270
BASIC_WAIT: equ 0x3958
271
BASIC_XOR: equ 0x3A1C
272
BASIC_ABS: equ 0x39E8
273
BASIC_ATN: equ 0x39F8
274
BASIC_ASC: equ 0x3A06
275
BASIC_BIN: equ 0x3A16
276
BASIC_CINT: equ 0x3A18
277
BASIC_CSNG: equ 0x3A1A
278
BASIC_CDBL: equ 0x3A1C
279
BASIC_CVI: equ 0x3A2C
280
BASIC_CVS: equ 0x3A2E
281
BASIC_CVD: equ 0x3A30
282
BASIC_COS: equ 0x39F4
283
BASIC_CHR: equ 0x3A08
284
BASIC_DSKF: equ 0x3A28
285
BASIC_EXP: equ 0x39F2
286
BASIC_EOF: equ 0x3A32
287
BASIC_FRE: equ 0x39FA
288
BASIC_FIX: equ 0x3A1E
289
BASIC_FPOS: equ 0x3A2A
290
BASIC_HEX: equ 0x3A12
291
BASIC_INT: equ 0x39E6
292
BASIC_INP: equ 0x39FC
293
BASIC_LPOS: equ 0x3A14
294
BASIC_LOG: equ 0x39F0
295
BASIC_LOC: equ 0x3A34
296
BASIC_LEN: equ 0x3A00
297
BASIC_LEFT: equ 0x39DE
298
BASIC_LOF: equ 0x3A36
299
BASIC_MKI: equ 0x3A38
300
BASIC_MKS: equ 0x3A3A
301
BASIC_MKD: equ 0x3A3C
302
BASIC_MID: equ 0x39E2
303
BASIC_OCT: equ 0x3A10
304
BASIC_POS: equ 0x39FE
305
BASIC_PEEK: equ 0x3A0A
306
BASIC_PDL: equ 0x3A24
307
BASIC_PAD: equ 0x3A26
308
BASIC_RIGHT: equ 0x39E0
309
BASIC_RND: equ 0x39EC
310
BASIC_SGN: equ 0x39E4
311
BASIC_SQR: equ 0x39EA
312
BASIC_SIN: equ 0x39EE
313
BASIC_STR: equ 0x3A02
314
BASIC_SPACE2: equ 0x3A0E
315
BASIC_STICK: equ 0x3A20
316
BASIC_STRIG: equ 0x3A22
317
BASIC_TAN: equ 0x39F6
318
BASIC_VAL: equ 0x3A04
319
BASIC_VPEEK: equ 0x3A0C
321
BASIC_TRAP_ENABLE: equ 0x631B ; ON INTERVAL/KEY/SPRITE/STOP/STRIG - hl = pointer to trap block
322
BASIC_TRAP_DISABLE: equ 0x632B ; hl = pointer to trap block
323
BASIC_TRAP_ACKNW: equ 0x6358 ; hl, acknowledge trap (handle trap: sts=5? has handler? ackn, pause, run trap, sts=1? unpause)
324
BASIC_TRAP_PAUSE: equ 0x6331 ; hl
325
BASIC_TRAP_UNPAUSE: equ 0x633E ; hl
326
BASIC_TRAP_CLEAR: equ 0x636E
328
BASIC_PLAY_DIRECT: equ 0x744C
329
BASIC_DRAW_DIRECT: equ 0x568C
331
BASIC_READYR: equ 0x409B
332
BASIC_READYC: equ 0x7D17
333
BASIC_FACEVAL: equ 0x4DC7
335
BASIC_ERROR_HANDLER:equ 0x406F
336
BASIC_ERROR_SYNTAX: equ 0x4055
337
BASIC_ERROR_DIVZER: equ 0x4058
338
BASIC_ERROR_OVRFLW: equ 0x4067
339
BASIC_ERROR_ARRAY: equ 0x405E
340
BASIC_ERROR_TYPMIS: equ 0x406D
342
; BASIC ERROR CODES TO BASIC_ERROR_HANDLER
343
; 01 NEXT without FOR 19 Device I/O error
344
; 02 Syntax error 20 Verify error
345
; 03 RETURN without GOSUB 21 No RESUME
346
; 04 Out of DATA 22 RESUME without error
347
; 05 Illegal function call 23 Unprintable error
348
; 06 Overflow 24 Missing operand
349
; 07 Out of memory 25 Line buffer overflow
350
; 08 Undefined line number 50 FIELD overflow
351
; 09 Subscript out of range 51 Internal error
352
; 10 Redimensioned array 52 Bad file number
353
; 11 Division by zero 53 File not found
354
; 12 Illegal direct 54 File already open
355
; 13 Type mismatch 55 Input past end
356
; 14 Out of string space 56 Bad file name
357
; 15 String too long 57 Direct statement in file
358
; 16 String formula too complex 58 Sequential I/O only
359
; 17 Can't CONTINUE 59 File not OPEN
360
; 18 Undefined user function
362
;---------------------------------------------------------------------------------------------------------
363
; MSX BASIC WORK AREAS
364
;---------------------------------------------------------------------------------------------------------
366
BASIC_DAC: equ 0xF7F6 ; 16
367
BASIC_ARG: equ 0xF847 ; 16
368
BASIC_VALTYP: equ 0xF663
369
BASIC_RNDX: equ 0xF857
370
BASIC_BUF: equ 0xF55E ; 259
371
BASIC_KBUF: equ 0xF41F ; 318
372
BASIC_SWPTMP: equ 0xF7BC ; 8
373
BASIC_STRBUF: equ 0xF7C5 ; 43
374
BASIC_TXTTAB: equ 0xF676
375
BASIC_VARTAB: equ 0xF6C2
376
BASIC_ARYTAB: equ 0xF6C4
377
BASIC_STREND: equ 0xF6C6
378
BASIC_STKTOP: equ 0xF674
379
BASIC_FRETOP: equ 0xF69B
380
BASIC_MEMSIZ: equ 0xF672
382
BASIC_TEMPPT: equ 0xF678 ; 2 Starting address of unused area of temporary descriptor.
383
BASIC_TEMPST: equ 0xF67A ; 30 Temporary descriptors.
385
BASIC_DATPTR: equ 0xF6C8 ; 2 Pointer to next data to read from the instruction DATA. Modified by RESTORE.
386
BASIC_DATLIN: equ 0xF6A3 ; 2 Número de linha do comando DATA para o comando READ.
387
BASIC_DORES: equ 0xF664 ; 1 Usada pelo comando DATA para manter o texto no formato ASCII.
388
BASIC_DEFTBL: equ 0xF6CA ; 26 table of variables defined by DEFINT, DEFSTR, DEFSNG and DEFDBL for each alphabet letter (2 = integer, 3 = String, 4 = Simple precision, 8 = Double precision).
390
BASIC_CURLIN: equ 0xF41C ; BASIC current line number
391
BASIC_INTVAL: equ 0xFCA0 ; interval value
392
BASIC_INTCNT: equ 0xFCA2 ; interval current count
394
BASIC_PRMPRV: equ 0xF74C ; Pointer to previous parameter block in PARM1
396
BASIC_TRPTBL: equ 0xFC4C ; 78 trap table - array of 3 bytes - state[1] (bit 0=on, bit 1=stop, bit 2=active) + address[2]
398
BASIC_TRPTBL_KEY: equ 0xFC4C ; 30 ON KEY GOSUB
399
BASIC_TRPTBL_STOP: equ 0xFC6A ; 3 ON STOP GOSUB
400
BASIC_TRPTBL_SPRITE: equ 0xFC6D ; 3 ON SPRITE GOSUB
401
BASIC_TRPTBL_STRIG: equ 0xFC70 ; 15 ON STRIG GOSUB
402
BASIC_TRPTBL_INTERVAL: equ 0xFC7F ; 3 ON INTERVAL GOSUB
403
BASIC_TRPTBL_OTHER: equ 0xFC82 ; 24 reserved for expansion
405
BASIC_ONGSBF: equ 0xFBD8 ; 1 trap occurred counter (0=not occurred)
409
;--------------------------------------------------------
411
;--------------------------------------------------------
413
;--------------------------------------------------------
415
;--------------------------------------------------------
416
COMPILE_TO_ROM: EQU 1
418
MACRO __call_basic,CALL_PARM
423
if defined COMPILE_TO_DOS
425
MACRO __call_bios,CALL_PARM
426
;ld iy,(BIOS_EXPTBL-1)
428
call BIOS_CALBAS ; BIOS_CALSLT
433
MACRO __call_bios,CALL_PARM
440
push hl ; save parameter
444
pop iy ; restore PC of caller
445
pop hl ; get next parameter
446
push iy ; save PC of caller
450
pop iy ; restore PC of caller
451
push hl ; save return parameter
452
push iy ; save PC of caller
456
pop iy ; restore PC of caller
457
push hl ; save return parameter
458
push iy ; save PC of caller
462
MACRO set.line.number, line_number
463
ld bc, line_number ; current line number
464
ld (BASIC_CURLIN), bc
468
ld a, (BIOS_INTFLG) ; verify CTRL+BREAK
474
ld a, (BASIC_ONGSBF) ; trap occured counter
480
;---------------------------------------------------------------------------------------------------------
482
;---------------------------------------------------------------------------------------------------------
484
romSize: equ 0x8000 ; ROM size (32k)
485
pageSize: equ 0x4000 ; Page size (16k)
486
lowLimitSize: equ 0x400 ; 10% of a page size
488
if defined COMPILE_TO_BIN
490
pgmArea: equ 0x8000 ; page 2 - program area
491
ramArea: equ 0xc000 ; page 3 - free RAM start area
493
org pgmArea ; program binary type start address
494
db 0FEh ; binary file ID
495
dw start_pgm ; begin address
496
dw end_file - 1 ; end address
497
dw start_pgm ; program execution address (for ,R option)
500
if defined COMPILE_TO_ROM
502
pgmArea: equ 0x4000 ; page 1 and 2 - program area
503
ramArea: equ 0xc000 ; page 3 - free RAM start area
505
org pgmArea ; program rom type start address
506
db 'AB' ; rom file ID
508
dw 0x0000 ; STATEMENT
515
pgmArea: equ 0x8000 ; page 2 - program area
516
ramArea: equ 0xc000 ; page 3 - free RAM start area
518
org pgmArea ; program DOS type start address ; 0x0100
524
;---------------------------------------------------------------------------------------------------------
526
;---------------------------------------------------------------------------------------------------------
528
if defined COMPILE_TO_ROM
531
call PROGRAM_SLOT_2_RESTORE
532
__call_basic BASIC_READYR ; warm start Basic
537
call PROGRAM_SLOT_GET
538
ld (BIOS_RAMAD2), a ; Save RAM slot of page 8000h-BFFFh
541
PROGRAM_SLOT_2_RESTORE:
544
jp BIOS_ENASLT ; Select the RAM on page 8000h-BFFFh
546
PROGRAM_SLOT_2_ENABLE:
548
call PROGRAM_SLOT_ENABLE_SUB
550
jp BIOS_ENASLT ; Select the ROM on page 8000h-BFFFh
552
PROGRAM_SLOT_1_ENABLE:
556
call PROGRAM_SLOT_ENABLE_SUB
558
jp BIOS_ENASLT ; Select the ROM on page 4000h-7FFFh
560
PROGRAM_SLOT_ENABLE_SUB:
563
and 3 ;Keep bits corresponding to the page
582
; a <- slot ID formatted FxxxSSPP
583
; Modifies: af, bc, de, hl
584
; ref: https://www.msx.org/forum/msx-talk/development/fusion-c-and-htimi#comment-366469
588
jr z,PrimaryShiftContinue
593
PrimaryShiftContinue:
595
jr z,PrimaryShiftDone
610
inc hl ; move to SLTTBL
617
jr z,SecondaryShiftContinue
622
SecondaryShiftContinue:
624
jr nz,SecondaryShiftDone
634
if defined COMPILE_TO_DOS
639
__call_bios BIOS_ENASLT ; Select main ROM on page 0 (0000h~3FFFh)
645
__call_bios BIOS_ENASLT ; Select main ROM on page 1 (4000h~7FFFh)
652
;---------------------------------------------------------------------------------------------------------
654
;---------------------------------------------------------------------------------------------------------
656
start_pgm: ; start of the program
658
if defined COMPILE_TO_DOS
660
call BIOS_SLOT_ENABLE ; enable bios on page 0
661
call BASIC_SLOT_ENABLE ; enable basic on page 1
664
if defined COMPILE_TO_ROM
666
call PROGRAM_SLOT_2_SAVE ; save slot on page 2
667
call PROGRAM_SLOT_2_ENABLE ; enable program on page 2
672
__call_bios BIOS_ERAFNK ; turn off function keys display
673
__call_bios BIOS_GICINI ; initialize sound system
674
__call_bios BIOS_INITXT ; initialize text screen
676
ld (BIOS_CLIKSW), a ; disable keyboard click
678
ld (BASIC_CURLIN), bc ; interpreter in direct mode
679
__call_basic BASIC_TRAP_CLEAR ; clear traps work space
680
;call INITIALIZE_PARAMETERS ; initialize parameters stack
681
call memory.init ; initialize memory allocation
682
call INITIALIZE_VARIABLES ; initialize variables
686
call CLS ; action call
689
ld hl, LIT_5 ; parameter
691
call PRINT ; action call
692
ld hl, PRINT.CRLF ; parameter
694
call PRINT ; action call
697
ld hl, LIT_7 ; parameter
699
ld hl, IDF_6 ; parameter
701
call LET ; action call
704
ld hl, IDF_6 ; parameter
706
ld hl, LIT_9 ; parameter
708
call MATH.ADD ; action call
709
ld hl, IDF_6 ; parameter
711
call LET ; action call
714
ld hl, IDF_6 ; parameter
716
ld hl, LIT_12 ; parameter
718
call MATH.MULT ; action call
719
ld hl, LIT_14 ; parameter
721
call MATH.ADD ; action call
722
ld hl, IDF_11 ; parameter
724
call LET ; action call
727
ld hl, IDF_6 ; parameter
729
call PRINT ; action call
730
ld hl, LIT_15 ; parameter
732
call PRINT ; action call
733
ld hl, IDF_11 ; parameter
735
call PRINT ; action call
736
ld hl, PRINT.CRLF ; parameter
738
call PRINT ; action call
743
;---------------------------------------------------------------------------------------------------------
745
;---------------------------------------------------------------------------------------------------------
747
end_pgm: __call_bios BIOS_DSPFNK ; turn on function keys display
749
ld (BIOS_CLIKSW), a ; enable keyboard click
751
if defined COMPILE_TO_ROM
754
__call_basic BASIC_READYR ; warm start Basic
757
ret ; end of the program
759
;__call_bios BIOS_GICINI ; initialize sound system
760
;if defined COMPILE_TO_DOS or defined COMPILE_TO_ROM
761
; __call_bios BIOS_RESET ; restart Basic
763
; __call_basic BASIC_END ; end to Basic
767
;---------------------------------------------------------------------------------------------------------
769
;---------------------------------------------------------------------------------------------------------
774
; out IX = variable assigned address
775
pop.parm ; get variable address parameter
776
push hl ; just to transfer hl to ix
778
ld a, (ix) ; get variable type
779
cp 3 ; test if string
780
jr nz, LET.PARM ; if not a string, it isn't necessary to free memory
781
ld a, (ix + 3) ; get variable string length
783
jr z, LET.PARM ; if zero, it isn't necessary to free memory
784
ld c, (ix + 4) ; get old string address low
785
ld b, (ix + 5) ; get old string address high
786
push ix ; save variable address
787
push bc ; just to transfer bc (old string address) to ix
789
call memory.free ; free memory
790
pop ix ; restore variable address
791
LET.PARM: pop.parm ; get data address parameter (out hl = data address)
792
ld a, (ix + 2) ; get variable type flag
793
or a ; cp 0 - test type flag (0=any, 255=fixed)
794
jr nz, LET.FIXED ; if type flag is fixed, so casting is necessary
795
LET.ANY: push ix ; just to transfer ix (variable address) to de
797
ldi ; copy 1 byte from hl (data address) to de (variable address)
798
inc de ; go to variable data area
800
inc hl ; go to data data area
802
ld bc, 8 ; data = 8 bytes
803
ldir ; copy bc bytes from hl (data address) to de (variable address)
804
ld a, (ix) ; get variable type
805
cp 3 ; test if string
806
ret nz ; if not string, return
807
jp LET.STRING ; else do string treatment (in ix = variable address)
808
LET.FIXED: push ix ; save variable destination address
809
push hl ; save variable source address
810
ld a, (ix) ; get variable fixed type, and hl has parameter data address
811
call CAST_TO ; cast data to type (in hl = variable address, a = type to, out hl = casted data address)
813
pop ix ; restore variable address
814
ld a, (ix) ; get variable destination type again
815
cp 3 ; test if string
816
jr nz, LET.VALUE ; if not string, do value treatment
817
ld a, (de) ; get variable source type again
818
cp 3 ; test if string
819
jr nz, LET.FIX1 ; if not string, get casted string size
824
ld (ix + 3), a ; source string size
827
call GET_STR.LENGTH ; get string length (in HL, out B)
829
ld (ix + 3), b ; set variable length
830
LET.FIX2: ld (ix + 4), l ; casted data address low
831
ld (ix + 5), h ; casted data address high
832
jp LET.STRING ; do string treatment (in ix = variable address)
833
LET.VALUE: push ix ; just to transfer ix (variable address) to de
835
inc de ; go to variable data area (and the data from its casted)
838
ld bc, 8 ; data = 8 bytes
839
ldir ; copy bc bytes from hl (data address) to de (variable address)
841
LET.STRING: ld a, (ix + 3) ; string size
842
or a ; cp 0 - test if null
843
jr nz, LET.ALLOC ; if not null, allocate new string (in ix = variable address)
844
ld bc, LIT_NULL_STR ; else, set to a null string literal
845
ld (ix + 4), c ; variable address low
846
ld (ix + 5), b ; variable address high
848
LET.ALLOC: push ix ; save variable address
849
ld l, (ix + 4) ; source string address low
850
ld h, (ix + 5) ; source string address high
851
push hl ; save copy from address
852
ld c, (ix + 3) ; get variable length
854
inc bc ; string length have one more byte from zero terminator
855
push bc ; save variable lenght + 1
856
call memory.alloc ; in bc = size, out ix = address, nz=OK
858
push ix ; just to transfer memory address from ix to de
860
pop bc ; restore bytes to be copied
861
pop hl ; restore copy from string address
862
push de ; save copy to address
863
ldir ; copy bc bytes from hl (data address) to de (variable address)
866
pop de ; restore copy to address
867
pop ix ; restore variable address
868
ld (ix + 4), e ; put memory address low into variable
869
ld (ix + 5), d ; put memory address high into variable
870
ret ; variable assigned
875
pop.parm ; get parameter boolean result in hl
878
ld a, (ix+5) ; put boolean integer result in a
884
if defined EXIST_DATA_SET
886
jp z, gfxClearTileScreen
889
__call_bios BIOS_CLS ; clear screen
895
pop.parm ; get first parameter
898
ret z ; return if string size zero
899
if defined EXIST_DATA_SET
900
ld (BIOS_TEMP), a ; size of string
904
; discard if first char < 32 or > 126
911
; adjust default color
924
call gfxSetTileDefaultColor
933
;call MATH.MULT.16 ; slow y * 32
960
call MATH.PARM.POP ; get parameters into DAC/ARG
961
ld a, (BASIC_VALTYP) ;
962
cp 2 ; test if integer
964
cp 3 ; test if string
965
jp z, STRING.CONCAT ;
966
cp 4 ; test if single
968
jp MATH.ADD.DBL ; it is a double
973
call MATH.PARM.POP ; get parameters into DAC/ARG
974
ld a, (BASIC_VALTYP) ;
975
cp 2 ; test if integer
976
jp z, MATH.MULT.INT ;
977
cp 3 ; test if string
979
cp 4 ; test if single
980
jp z, MATH.MULT.SGL ;
981
jp MATH.MULT.DBL ; it is a double
985
; abstract virtual GOTO
988
;---------------------------------------------------------------------------------------------------------
989
; MSX BASIC SUPPORT CODE
990
;---------------------------------------------------------------------------------------------------------
992
if defined ON_ERROR or defined ON_INTERVAL or defined ON_KEY_START or defined ON_SPRITE or defined ON_STOP or defined ON_STRIG_START or defined TRAP_ENABLED or defined TRAP_DISABLED or defined TRAP_PAUSE or defined TRAP_UNPAUSE
1007
; in hl = trap block address (handle trap: sts=5? has handler? ackn, pause, run trap, sts=1? unpause)
1009
ld a, (hl) ; trap status
1010
cp 5 ; trap occured AND trap not paused AND trap enabled ?
1011
ret nz ; return if false
1013
ld e, (hl) ; get trap address
1020
ret z ; return if address zero
1022
__call_basic BASIC_TRAP_ACKNW
1023
__call_basic BASIC_TRAP_PAUSE
1024
ld hl, TRAP_HANDLER.1
1025
ld a, (BASIC_ONGSBF) ; save traps execution
1028
ld (BASIC_ONGSBF), a ; disable traps execution
1029
push hl ; next return will be to trap handler
1030
push de ; indirect jump to trap address
1032
TRAP_HANDLER.1: pop af
1033
ld (BASIC_ONGSBF), a ; restore traps execution
1036
cp 1 ; trap enabled?
1038
__call_basic BASIC_TRAP_UNPAUSE
1041
; hl = trap block, de = trap handler
1043
ld (hl), a ; trap block status
1045
ld (hl), e ; trap block handler (pointer)
1052
if defined SET_PLAY_VOICE_1 or defined SET_PLAY_VOICE_2 or defined SET_PLAY_VOICE_3 or defined DO_PLAY or defined MUSIC_PLAY or defined MUSIC_NEXT or defined MUSIC_STOP
1055
ld (BIOS_TEMP), a ; save voice number
1059
ret nz ; return if not string
1062
ld (BIOS_TEMP2), a ; save string size
1063
push hl ; string address
1064
ld a, (BIOS_TEMP) ; restore voice number
1065
call BIOS_GETVCP ; get PSG voice buffer address (in A = voice number, out HL = address of byte 2)
1067
ld a, (BIOS_TEMP2) ; restore string size
1068
ld (hl), a ; string size
1070
ld (hl), e ; string address
1074
ld D,H ; voice stack
1089
ld hl, BIOS_TEMP ; voice count
1103
__call_basic BASIC_PLAY_DIRECT
1110
;---------------------------------------------------------------------------------------------------------
1111
; VARIABLES ROUTINES
1112
;---------------------------------------------------------------------------------------------------------
1114
; input hl = variable address
1115
; input bc = variable name
1116
; input d = variable type
1117
INIT_VAR: ld (hl), d ; variable type
1119
ld (hl), c ; variable name 1
1121
ld (hl), b ; variable name 2
1135
CLEAR.VAR.LOOP: inc hl
1136
ld (hl), 0 ; data address/value
1139
; input HL = variable address
1140
; input A = variable output type
1141
; output HL = casted data address
1151
; input HL = variable address
1152
; output HL = variable address
1153
CAST_TO.INT: ;push af
1158
jp z, CAST_STR_TO.INT
1160
jp z, CAST_SGL_TO.INT
1162
jp z, CAST_DBL_TO.INT
1165
; input HL = variable address
1166
; output HL = variable address
1167
CAST_TO.STR: ;push af
1170
jp z, CAST_INT_TO.STR
1174
jp z, CAST_SGL_TO.STR
1176
jp z, CAST_DBL_TO.STR
1179
; input HL = variable address
1180
; output HL = variable address
1181
CAST_TO.SGL: ;push af
1184
jp z, CAST_INT_TO.SGL
1186
jp z, CAST_STR_TO.SGL
1190
jp z, CAST_DBL_TO.SGL
1193
; input HL = variable address
1194
; output HL = variable address
1195
CAST_TO.DBL: ;push af
1198
jp z, CAST_INT_TO.DBL
1200
jp z, CAST_STR_TO.DBL
1202
jp z, CAST_SGL_TO.DBL
1207
CAST_SGL_TO.STR: ; same as CAST_INT_TO.STR
1208
CAST_DBL_TO.STR: ; same as CAST_INT_TO.STR
1209
CAST_INT_TO.STR: call COPY_TO.DAC
1211
__call_bios MATH_FOUT ; convert DAC to string
1214
CAST_INT_TO.SGL: call COPY_TO.DAC
1215
__call_bios MATH_FRCSGL
1218
CAST_INT_TO.DBL: call COPY_TO.DAC
1219
__call_bios MATH_FRCDBL
1222
CAST_SGL_TO.INT: ; same as CAST_DBL_TO.INT
1223
CAST_DBL_TO.INT: call COPY_TO.DAC
1224
__call_bios MATH_FRCINT
1227
CAST_STR_TO.INT: call CAST_STR_TO.VAL ;
1228
__call_bios MATH_FRCINT ;
1231
CAST_STR_TO.SGL: call CAST_STR_TO.VAL ;
1232
__call_bios MATH_FRCSGL ;
1235
CAST_STR_TO.DBL: call CAST_STR_TO.VAL ;
1236
__call_bios MATH_FRCDBL ;
1239
CAST_STR_TO.VAL: call GET_STR.ADDR ;
1241
__call_bios MATH_FIN ; convert string to a value type
1244
GET_INT.VALUE: inc hl ; output BC with integer value
1250
CAST_SGL_TO.DBL: ; same as GET_DBL.ADDR
1251
CAST_DBL_TO.SGL: ; same as GET_DBL.ADDR
1252
GET_INT.ADDR: ; same as GET_DBL.ADDR
1253
GET_SGL.ADDR: ; same as GET_DBL.ADDR
1254
GET_DBL.ADDR: inc hl
1259
GET_STR.ADDR: push hl
1265
; input hl = string address
1266
; output b = string length
1267
GET_STR.LENGTH: ld b, 0
1268
GET_STR.LEN.NEXT: ld a, (hl)
1275
jr z, GET_STR.LEN.ERR
1277
GET_STR.LEN.ERR: ld b, 0
1279
STRING.COMPARE: ld ix, (BASIC_DAC+1) ; string 1
1280
ld iy, (BASIC_ARG+1) ; string 2
1281
STRING.COMPARE.NX: ld a, (ix) ; next char from string 1
1282
cp (iy) ; char s1 = char s2?
1283
jr nz, STRING.COMPARE.NE ; if not equal...
1285
jr z, STRING.COMPARE.F1 ; if string 1 has finished...
1286
ld a, (iy) ; next char from string 2
1288
jr z, STRING.COMPARE.GT ; if s2 has finished, s1 has not finished yet, so s1 is greater than s2
1291
jr STRING.COMPARE.NX ; get next char pair
1292
STRING.COMPARE.F1: ld a, (iy) ; verify if string 2 has finished too
1294
jr z, STRING.COMPARE.EQ ; if s2 has finished, then they are equals
1295
jr STRING.COMPARE.LT ; else, result = s1 is less than s2
1296
STRING.COMPARE.NE: jr c, STRING.COMPARE.GT ; verify if s1 is greater than s2...
1297
STRING.COMPARE.LT: ld a, 1 ; ...else, result = s1 less than s2
1299
STRING.COMPARE.GT: ld a, 0xFF ; result = s1 is greater than s2
1301
STRING.COMPARE.EQ: xor a ; result = s1 is equal to s2
1303
STRING.CONCAT: ld ix, BASIC_DAC ; s1 size
1304
ld a, (BASIC_ARG) ; s2 size
1305
add a, (ix) ; s3 size = s1 size + s2 size
1309
inc bc ; add 1 byte to size
1310
call memory.alloc ; in bc size, out ix new memory address, nz=OK
1311
jp z, memory.error ;
1315
ld a, (BASIC_DAC) ; s1 size
1316
ld hl, (BASIC_DAC + 1) ; string 1
1317
call COPY_TO.STR ; copy to new memory
1318
ld a, (BASIC_ARG) ; s2 size
1319
ld hl, (BASIC_ARG + 1) ; string 2
1320
call COPY_TO.STR ; copy to new memory
1322
ld (de), a ; null terminated
1325
call COPY_TO.VAR_DUMMY.STR ;
1326
ret.parm ; WARNING - VERIFY STRING MEMORY LEAKs
1327
STRING.PRINT: ld a, (BIOS_SCRMOD) ; 0=40x24 Text Mode, 1=32x24 Text Mode, 2=Graphics Mode, 3=Multicolour Mode
1329
jr nc, STRING.PRINT.G2 ; jump if graphic screen mode MSX2 (>=5)
1331
jr nc, STRING.PRINT.G1 ; jump if graphic screen mode MSX1 (>=2)
1332
STRING.PRINT.T: ld a, (hl) ; get a char from a string parameter
1333
or a ; cp 0 - is it the string end?
1335
__call_bios BIOS_CHPUT ; put the char (a) into text screen
1337
jr STRING.PRINT.T ; repeat
1338
STRING.PRINT.G1: ld a, (hl) ; get a char from a string parameter
1339
or a ; cp 0 - is it the string end?
1341
__call_bios BIOS_GRPPRT ; put the char (a) into graphical screen
1343
jr STRING.PRINT.G1 ; repeat
1344
STRING.PRINT.G2: ld a, (hl) ; get a char from a string parameter
1345
or a ; cp 0 - is it the string end?
1347
ld ix, BIOS_GRPPRT2 ; put the char (a) into graphical screen
1350
jr STRING.PRINT.G2 ; repeat
1352
; a = string size to copy
1353
; input hl = string from
1354
; input de = string to
1356
ret z ; avoid copy if size = zero
1358
ld c, a ; string size
1359
ldir ; copy bc bytes from hl to de
1361
COPY_TO.BASIC_BUF: ld bc, BASIC_BUF
1362
ld a, (LIT_QUOTE_CHAR)
1365
COPY_BAS_BUF.LOOP: ld a, (hl)
1367
jr z, COPY_BAS_BUF.EXIT
1371
jr COPY_BAS_BUF.LOOP
1372
COPY_BAS_BUF.EXIT: ld a, (LIT_QUOTE_CHAR)
1379
COPY_TO.VAR_DUMMY: ld a, (BASIC_VALTYP) ; create dummy variable from VALTYPE
1381
jr nz, COPY_TO.VAR_DUMMY.DBL
1383
call GET_STR.LENGTH ; get string length
1385
ld a, b ; string length
1386
COPY_TO.VAR_DUMMY.STR: call GET_VAR_DUMMY.ADDR ; create dummy string variable from HL
1387
ld (ix), 3 ; data type string
1389
ld (ix+2), 255 ; var type fixed
1390
ld (ix+3), a ; string length
1391
ld (ix+4), l ; data address low
1392
ld (ix+5), h ; data address high
1393
;call GET_STR.LENGTH ; get string length
1394
;ld (ix+3), b ; string length
1395
push ix ; output var address...
1398
COPY_TO.VAR_DUMMY.INT: call GET_VAR_DUMMY.ADDR ; create dummy integer variable from BC
1399
ld (ix), 2 ; data type string
1410
push ix ; output var address...
1413
COPY_TO.VAR_DUMMY.DBL: call GET_VAR_DUMMY.ADDR ; create dummy value variable from DAC
1414
ld (ix), a ; data type
1419
push ix ; just to copy ix to de
1424
ldir ; copy bc bytes from hl (data address) to de (variable address)
1425
push ix ; output var address...
1428
GET_VAR_DUMMY.ADDR: push af ;
1431
ld ix, (VAR_DUMMY.POINTER) ;
1432
ld a, (VAR_DUMMY.COUNTER) ;
1433
GET_VAR_DUMMY.NEXT: add ix, de ;
1436
jr nz, GET_VAR_DUMMY.EXIT ;
1438
ld ix, VAR_DUMMY.DATA ;
1439
GET_VAR_DUMMY.EXIT: ld (VAR_DUMMY.POINTER), ix ;
1440
ld (VAR_DUMMY.COUNTER), a ;
1441
ld a, (ix) ; get last var dummy type
1442
cp 3 ; is it string?
1443
call z, GET_VAR_DUMMY.FREE ; free string memory
1450
ld l, (ix+4) ; get string data address
1454
call memory.free ; free memory
1458
; input hl = variable address
1459
COPY_TO.DAC: ld de, BASIC_DAC
1460
COPY_TO.DAC.DATA: ld a, (hl)
1461
ld (BASIC_VALTYP), a
1465
ld bc, 8 ; data = 8 bytes
1466
ldir ; copy bc bytes from hl (data address) to de (variable address)
1468
COPY_TO.ARG: ld de, BASIC_ARG ;
1469
jr COPY_TO.DAC.DATA ;
1470
COPY_TO.DAC_ARG: ld hl, BASIC_DAC ;
1472
ld bc, 8 ; data = 8 bytes
1473
ldir ; copy bc bytes from hl (data address) to de (variable address)
1475
COPY_TO.ARG_DAC: ld hl, BASIC_ARG ;
1477
ld bc, 8 ; data = 8 bytes
1478
ldir ; copy bc bytes from hl (data address) to de (variable address)
1480
COPY_TO.DAC_TMP: ld hl, BASIC_DAC ;
1481
ld de, BASIC_SWPTMP ;
1482
ld bc, 8 ; data = 8 bytes
1483
ldir ; copy bc bytes from hl (data address) to de (variable address)
1485
COPY_TO.TMP_DAC: ld hl, BASIC_SWPTMP ;
1487
ld bc, 8 ; data = 8 bytes
1488
ldir ; copy bc bytes from hl (data address) to de (variable address)
1491
exx ; save registers
1494
ld de, BASIC_SWPTMP ;
1495
ldir ; copy bc bytes from hl to de
1499
ldir ; copy bc bytes from hl to de
1501
ld hl, BASIC_SWPTMP ;
1503
ldir ; copy bc bytes from hl to de
1504
exx ; restore registers
1507
CLEAR.DAC: ld de, BASIC_DAC
1508
CLEAR.DAC.DATA: ld hl, BASIC_VALTYP
1511
ld bc, 8 ; data = 8 bytes
1512
ldir ; copy bc bytes from hl (data address) to de (variable address)
1514
CLEAR.ARG: ld de, BASIC_ARG
1519
;---------------------------------------------------------------------------------------------------------
1520
; MATH 16 BITS ROUTINES
1521
;---------------------------------------------------------------------------------------------------------
1523
MATH.PARM.POP: pop af ; get PC from caller stack
1524
ex af, af' ; save PC to temp
1525
pop.parm ; get first parameter
1526
call COPY_TO.ARG ; put HL in ARG (return var type in A)
1527
pop.parm ; get second parameter
1528
ex af, af' ; restore PC from temp
1529
push af ; put again PC from caller in stack
1530
ex af, af' ; restore 1st data type
1531
push af ; save 1st data type
1532
call COPY_TO.DAC ; put HL in DAC (return var type in A)
1533
pop bc ; restore 1st data type (ARG) in B
1534
cp b ; test if data type in A (DAC) = data type in B (ARG)
1535
ret z ; return if is equal data types
1536
MATH.PARM.CAST: push bc ; else cast both to double
1537
and 12 ; test if single/double
1538
jr nz, MATH.PARM.CST1 ; avoid cast if already single/double
1539
__call_bios MATH_FRCDBL ; convert DAC to double
1540
MATH.PARM.CST1: pop af ;
1541
and 12 ; test if single/double
1542
jr nz, MATH.PARM.CST2 ; avoid cast if already single/double
1543
ld (BASIC_VALTYP), a ;
1544
call COPY_TO.DAC_TMP ;
1545
call COPY_TO.ARG_DAC ;
1546
__call_bios MATH_FRCDBL ; convert ARG to double
1547
call COPY_TO.DAC_ARG ;
1548
call COPY_TO.TMP_DAC ;
1549
MATH.PARM.CST2: ld a, 8 ;
1550
ld (BASIC_VALTYP), a ;
1552
MATH.PARM.POP.INT: ; return result in DAC/ARG as integer
1553
pop af ; get PC from caller stack
1554
ex af, af' ; save PC to temp
1555
pop.parm ; get first parameter
1556
ld a, (hl) ; get parameter type
1557
and 2 ; test if integer
1558
jr z, MATH.PARM.POP.I1 ; do cast if not integer
1559
call COPY_TO.ARG ; put HL in ARG (return var type in A)
1560
jr MATH.PARM.POP.I2 ; go to next parameter
1561
MATH.PARM.POP.I1: call COPY_TO.DAC ; put HL in DAC (return var type in A)
1562
__call_bios MATH_FRCINT ; convert DAC to int
1563
call COPY_TO.DAC_ARG ; copy DAC to ARG
1564
MATH.PARM.POP.I2: pop.parm ; get second parameter
1565
call COPY_TO.DAC ; put HL in DAC (return var type in A)
1566
and 2 ; test if integer
1567
jr nz, MATH.PARM.POP.I3 ; avoid cast if already integer
1568
__call_bios MATH_FRCINT ; convert DAC to int
1570
ld (BASIC_VALTYP), a ;
1572
ex af, af' ; restore PC from temp
1573
push af ; put again PC from caller in stack
1575
MATH.PARM.PUSH: call COPY_TO.VAR_DUMMY ;
1581
; output in parm stack
1582
; http://www.z80.info/zip/zaks_book.pdf - page 104
1583
MATH.ADD.INT: ld hl, (BASIC_DAC+2) ;
1584
ld bc, (BASIC_ARG+2) ;
1586
ld (BASIC_DAC+2), hl ;
1591
if defined MATH.SUB or defined MATH.NEG
1594
; output in parm stack
1595
; http://www.z80.info/zip/zaks_book.pdf - page 104
1596
MATH.SUB.INT: ld hl, (BASIC_DAC+2) ;
1597
ld de, (BASIC_ARG+2) ;
1600
ld (BASIC_DAC+2), hl ;
1605
if defined MATH.MULT
1608
; output in parm stack
1609
MATH.MULT.INT: ld hl, (BASIC_DAC+2) ;
1610
ld bc, (BASIC_ARG+2) ;
1612
ld (BASIC_DAC+2), hl ;
1615
; input HL = multiplicand
1616
; input BC = multiplier
1617
; output HL = result
1618
; http://www.z80.info/zip/zaks_book.pdf - page 131
1619
MATH.MULT.16: ld a, c ; low multiplier
1620
ld c, b ; high multiplier
1622
ld d, h ; multiplicand
1625
MULT16LOOP: srl c ; right shift multiplier high
1626
rra ; rotate right multiplier low
1627
jr nc, MULT16NOADD ; test carry
1628
add hl, de ; add multiplicand to result
1629
MULT16NOADD: ex de, hl
1630
add hl, hl ; double - shift multiplicand
1637
if defined MATH.DIV or defined MATH.IDIV or defined MATH.MOD
1639
; input AC = dividend
1640
; input DE = divisor
1641
; output AC = quotient
1642
; output HL = remainder
1643
; http://www.z80.info/zip/zaks_book.pdf - page 140
1644
MATH.DIV.16: ld hl, 0 ; clear accumulator
1645
ld b, 16 ; set counter
1646
DIV16LOOP: rl c ; rotate accumulator result left
1648
adc hl, hl ; left shift
1649
sbc hl, de ; trial subtract divisor
1650
jr nc, $ + 3 ; subtract was OK ($ = current location)
1651
add hl, de ; restore accumulator
1652
ccf ; calculate result bit
1653
djnz DIV16LOOP ; counter not zero
1654
rl c ; shift in last result bit
1660
if defined GFX_FAST or defined LINE
1662
; compare two signed 16 bits integers
1663
; HL < DE: Carry flag
1664
; HL = DE: Zero flag
1665
; http://www.z80.info/zip/zaks_book.pdf - page 531
1666
MATH.COMP.S16: ld a, h ; test high order byte
1667
and 0x80 ; test sign, clear carry
1668
jr nz, MATH.COMP.S16.NEGM1 ; jump if hl is negative
1670
ret nz ; de is negative (and hl is positive)
1672
cp d ; signs are both positive, so normal compare
1674
ld a, l ; test low order byte
1677
MATH.COMP.S16.NEGM1:
1679
rla ; sign bit into carry
1680
ret c ; signs different
1682
cp d ; both signs negative
1692
MATH.ADD.SGL: ld a, 8 ;
1693
ld (BASIC_VALTYP), a ;
1694
MATH.ADD.DBL: __call_bios MATH_DECADD ;
1699
if defined MATH.SUB or defined MATH.NEG
1701
MATH.SUB.SGL: ld a, 8 ;
1702
ld (BASIC_VALTYP), a ;
1703
MATH.SUB.DBL: __call_bios MATH_DECSUB ;
1708
if defined MATH.MULT
1710
MATH.MULT.SGL: ld a, 8 ;
1711
ld (BASIC_VALTYP), a ;
1712
MATH.MULT.DBL: __call_bios MATH_DECMUL ;
1720
; output in parm stack
1721
MATH.DIV.INT: __call_bios MATH_FRCDBL ; convert DAC to double
1724
ld (BASIC_VALTYP), a ;
1725
__call_bios MATH_FRCDBL ; convert ARG to double
1727
MATH.DIV.SGL: ld a, 8 ;
1728
ld (BASIC_VALTYP), a ;
1729
MATH.DIV.DBL: __call_bios MATH_DECDIV ;
1734
if defined MATH.IDIV
1737
; output in parm stack
1738
MATH.IDIV.SGL: ld a, 8 ;
1739
ld (BASIC_VALTYP), a ;
1740
MATH.IDIV.DBL: __call_bios MATH_FRCINT ; convert DAC to integer
1743
ld (BASIC_VALTYP), a ;
1744
__call_bios MATH_FRCINT ; convert ARG to integer
1746
MATH.IDIV.INT: ld hl, (BASIC_DAC+2) ;
1749
ld de, (BASIC_ARG+2) ;
1753
ld (BASIC_DAC+2), hl ; quotient
1760
MATH.POW.INT: ld (BASIC_VALTYP), a ;
1761
__call_bios MATH_FRCDBL ; convert DAC to double
1764
ld (BASIC_VALTYP), a ;
1765
__call_bios MATH_FRCDBL ; convert ARG to double
1767
MATH.POW.SGL: ld a, 8 ;
1768
ld (BASIC_VALTYP), a ;
1769
MATH.POW.DBL: __call_bios MATH_DBLEXP ;
1776
;MATH.MOD.SGL: ld a, 8 ;
1777
; ld (BASIC_VALTYP), a ;
1778
;MATH.MOD.DBL: __call_bios MATH_FRCINT ; convert DAC to integer
1779
; call SWAP.DAC.ARG ;
1781
; ld (BASIC_VALTYP), a ;
1782
; __call_bios MATH_FRCINT ; convert ARG to integer
1783
; call SWAP.DAC.ARG ;
1784
MATH.MOD.INT: ld hl, (BASIC_DAC+2) ;
1787
ld de, (BASIC_ARG+2) ;
1789
ld (BASIC_DAC+2), hl ; remainder
1796
; fast 16-bit integer square root
1797
; http://www.retroprogramming.com/2017/07/a-fast-z80-integer-square-root.html
1798
; 92 bytes, 344-379 cycles (average 362)
1799
; v2 - 3 t-state optimization spotted by Russ McNulty
1800
; call with hl = number to square root
1801
; returns a = square root
1878
if defined RANDOMIZE or defined SEED
1880
MATH.RANDOMIZE: di ;
1881
ld bc, (BIOS_JIFFY) ;
1884
MATH.SEED: ld (BASIC_RNDX), bc ; seed to IRND
1885
push bc ; in bc = new integer seed
1889
ld (BASIC_DAC+2), bc ; copy bc to dac
1890
ld a, 2 ; type integer
1891
ld (BASIC_VALTYP), a ;
1892
__call_bios MATH_FRCDBL ; convert DAC integer to DAC double
1893
__call_bios MATH_NEG ; DAC = -DAC
1894
__call_bios MATH_RND ; put in DAC a new random number from previous DAC parameter
1899
MATH.ERROR: ld e, 13 ; type mismatch
1900
__call_basic BASIC_ERROR_HANDLER ;
1904
;---------------------------------------------------------------------------------------------------------
1906
;---------------------------------------------------------------------------------------------------------
1908
BOOLEAN.RET.TRUE: ld hl, LIT_TRUE ;
1910
BOOLEAN.RET.FALSE: ld hl, LIT_FALSE ;
1912
BOOLEAN.CMP.INT: ld hl, (BASIC_DAC+2) ;
1913
ld de, (BASIC_ARG+2) ;
1914
__call_bios MATH_ICOMP ;
1916
BOOLEAN.CMP.SGL: ld bc, (BASIC_ARG) ;
1917
ld de, (BASIC_ARG+2) ;
1918
__call_bios MATH_DCOMP ;
1920
BOOLEAN.CMP.DBL: __call_bios MATH_XDCOMP ;
1922
BOOLEAN.CMP.STR: call STRING.COMPARE ;
1925
if defined BOOLEAN.GT
1927
BOOLEAN.GT.INT: call BOOLEAN.CMP.INT ;
1929
BOOLEAN.GT.STR: call BOOLEAN.CMP.STR ;
1931
BOOLEAN.GT.SGL: call BOOLEAN.CMP.SGL ;
1933
BOOLEAN.GT.DBL: call BOOLEAN.CMP.DBL ;
1935
BOOLEAN.GT.RET: cp 0x01 ;
1936
jp z, BOOLEAN.RET.TRUE ;
1937
jp BOOLEAN.RET.FALSE ;
1940
if defined BOOLEAN.LT
1942
BOOLEAN.LT.INT: call BOOLEAN.CMP.INT ;
1944
BOOLEAN.LT.STR: call BOOLEAN.CMP.STR ;
1946
BOOLEAN.LT.SGL: call BOOLEAN.CMP.SGL ;
1948
BOOLEAN.LT.DBL: call BOOLEAN.CMP.DBL ;
1950
BOOLEAN.LT.RET: cp 0xFF ;
1951
jp z, BOOLEAN.RET.TRUE ;
1952
jp BOOLEAN.RET.FALSE ;
1956
if defined BOOLEAN.GE
1958
BOOLEAN.GE.INT: call BOOLEAN.CMP.INT ;
1960
BOOLEAN.GE.STR: call BOOLEAN.CMP.STR ;
1962
BOOLEAN.GE.SGL: call BOOLEAN.CMP.SGL ;
1964
BOOLEAN.GE.DBL: call BOOLEAN.CMP.DBL ;
1966
BOOLEAN.GE.RET: cp 0x01 ;
1967
jp z, BOOLEAN.RET.TRUE ;
1969
jp z, BOOLEAN.RET.TRUE ;
1970
jp BOOLEAN.RET.FALSE ;
1974
if defined BOOLEAN.LE
1976
BOOLEAN.LE.INT: call BOOLEAN.CMP.INT ;
1978
BOOLEAN.LE.STR: call BOOLEAN.CMP.STR ;
1980
BOOLEAN.LE.SGL: call BOOLEAN.CMP.SGL ;
1982
BOOLEAN.LE.DBL: call BOOLEAN.CMP.DBL ;
1984
BOOLEAN.LE.RET: cp 0xFF ;
1985
jp z, BOOLEAN.RET.TRUE ;
1987
jp z, BOOLEAN.RET.TRUE ;
1988
jp BOOLEAN.RET.FALSE ;
1992
if defined BOOLEAN.NE
1994
BOOLEAN.NE.INT: call BOOLEAN.CMP.INT ;
1996
BOOLEAN.NE.STR: call BOOLEAN.CMP.STR ;
1998
BOOLEAN.NE.SGL: call BOOLEAN.CMP.SGL ;
2000
BOOLEAN.NE.DBL: call BOOLEAN.CMP.DBL ;
2002
BOOLEAN.NE.RET: or a ; cp 0
2003
jp nz, BOOLEAN.RET.TRUE ;
2004
jp BOOLEAN.RET.FALSE ;
2008
if defined BOOLEAN.EQ
2010
BOOLEAN.EQ.INT: call BOOLEAN.CMP.INT ;
2012
BOOLEAN.EQ.STR: call BOOLEAN.CMP.STR ;
2014
BOOLEAN.EQ.SGL: call BOOLEAN.CMP.SGL ;
2016
BOOLEAN.EQ.DBL: call BOOLEAN.CMP.DBL ;
2018
BOOLEAN.EQ.RET: or a ; cp 0
2019
jp z, BOOLEAN.RET.TRUE ;
2020
jp BOOLEAN.RET.FALSE ;
2024
if defined BOOLEAN.AND
2026
BOOLEAN.AND.INT: ld a, (BASIC_DAC+2) ;
2027
ld hl, BASIC_ARG+2 ;
2029
ld (BASIC_DAC+2), a ;
2031
ld a, (BASIC_DAC+3) ;
2033
ld (BASIC_DAC+3), a ;
2039
if defined BOOLEAN.OR
2041
BOOLEAN.OR.INT: ld a, (BASIC_DAC+2) ;
2042
ld hl, BASIC_ARG+2 ;
2044
ld (BASIC_DAC+2), a ;
2046
ld a, (BASIC_DAC+3) ;
2048
ld (BASIC_DAC+3), a ;
2054
if defined BOOLEAN.XOR
2056
BOOLEAN.XOR.INT: ld a, (BASIC_DAC+2) ;
2057
ld hl, BASIC_ARG+2 ;
2059
ld (BASIC_DAC+2), a ;
2061
ld a, (BASIC_DAC+3) ;
2063
ld (BASIC_DAC+3), a ;
2069
if defined BOOLEAN.EQV
2071
BOOLEAN.EQV.INT: ld a, (BASIC_DAC+2) ;
2072
ld hl, BASIC_ARG+2 ;
2075
ld (BASIC_DAC+2), a ;
2077
ld a, (BASIC_DAC+3) ;
2080
ld (BASIC_DAC+3), a ;
2086
if defined BOOLEAN.IMP
2088
BOOLEAN.IMP.INT: ld a, (BASIC_DAC+2) ;
2089
ld hl, BASIC_ARG+2 ;
2092
ld (BASIC_DAC+2), a ;
2094
ld a, (BASIC_DAC+3) ;
2097
ld (BASIC_DAC+3), a ;
2103
if defined BOOLEAN.SHR
2105
BOOLEAN.SHR.INT: ld ix, BASIC_DAC+2 ; shift DAC integer to right (bits 15...0-->)
2106
ld a, (BASIC_ARG+2) ;
2108
jp z, MATH.PARM.PUSH ; return if not shift
2109
ld b, a ; shift count
2110
BOOLEAN.SHR.INT.N: rr (ix+1) ;
2113
djnz BOOLEAN.SHR.INT.N ; next shift
2115
jp MATH.PARM.PUSH ; return DAC
2119
if defined BOOLEAN.SHL
2121
BOOLEAN.SHL.INT: ld ix, BASIC_DAC+2 ; shift DAC integer to left (<--bits 15...0)
2122
ld a, (BASIC_ARG+2) ;
2124
jp z, MATH.PARM.PUSH ; return if not shift
2125
ld b, a ; shift count
2126
BOOLEAN.SHL.INT.N: rl (ix) ;
2129
djnz BOOLEAN.SHL.INT.N ; next shift
2131
jp MATH.PARM.PUSH ; return DAC
2135
if defined BOOLEAN.NOT
2137
BOOLEAN.NOT.INT: ld a, (BASIC_DAC+2) ;
2139
ld (BASIC_DAC+2), a ;
2140
ld a, (BASIC_DAC+3) ;
2142
ld (BASIC_DAC+3), a ;
2150
;---------------------------------------------------------------------------------------------------------
2151
; MEMORY ALLOCATION ROUTINES
2152
;---------------------------------------------------------------------------------------------------------
2153
; Adapted from memory allocator code by SamSaga2, Spain, 2015
2154
; https://www.msx.org/forum/msx-talk/development/asm-memory-allocator
2155
; https://www.msx.org/users/samsaga2
2156
;---------------------------------------------------------------------------------------------------------
2157
memory.heap_start: equ VAR_STACK.END + 1 ; start at end of variable stack
2158
memory.heap_end: equ 0xF0A0 - 100 ; end at start of work area for stack (100 bytes reserved), BIOS and BASIC interpreter
2159
block.next: equ 0 ; next free block address
2160
block.size: equ 2 ; size of block including header
2161
block: equ 4 ; block.next + block.size
2165
ld ix,memory.heap_start ; first block
2166
ld hl,memory.heap_start+block ; second block
2167
;; first block NEXT=secondblock, SIZE=0
2168
;; with this block we have a fixed start location
2169
;; because never will be allocated
2170
ld (ix+block.next),l
2171
ld (ix+block.next+1),h
2172
ld (ix+block.size),0
2173
ld (ix+block.size+1),0
2174
;; second block NEXT=0, SIZE=all
2175
;; the first and only free block have all available memory
2176
ld (ix+block.next+block),0
2177
ld (ix+block.next+block+1),0
2179
;ld hl,memory.heap_end ; size = @heap_end (stack) - heap_start - block_header * 2 - 100 (buffer for stack)
2182
ld de, memory.heap_start + (block * 2) + 100
2184
;ld de, block * 2 + 100
2186
ld (ix+block.size+block),l
2187
ld (ix+block.size+block+1),h
2191
;; IN BC=size, OUT IX=memptr, NZ=ok
2199
ld ix,memory.heap_start ; this
2202
ld l,(ix+block.size)
2203
ld h,(ix+block.size+1)
2206
jp z, memory.alloc.exactfit
2207
jp c, memory.alloc.nextblock
2208
;; split found block
2209
memory.alloc.splitfit:
2210
;; free space must allow at least two blocks headers (current + next)
2212
jr nz, memory.alloc.splitfit.do ; if free space > 0xFF, do split
2215
jr c, memory.alloc.nextblock ; if free space < 4, skip to next block
2216
memory.alloc.splitfit.do:
2217
;; newfreeblock = this + BC
2221
;; prevblock->next = newfreeblock
2222
ld (iy+block.next),l
2223
ld (iy+block.next+1),h
2224
;; newfreeblock->next = this->next
2226
pop iy ; iy = newfreeblock
2227
ld l,(ix+block.next)
2228
ld h,(ix+block.next+1)
2229
ld (iy+block.next),l
2230
ld (iy+block.next+1),h
2231
;; newfreeblock->size = this->size - BC
2232
ld l,(ix+block.size)
2233
ld h,(ix+block.size+1)
2236
ld (iy+block.size),l
2237
ld (iy+block.size+1),h
2239
ld (ix+block.size),c
2240
ld (ix+block.size+1),b
2242
;; use whole found block
2243
memory.alloc.exactfit:
2244
;; prevblock->next = this->next - remove block from free list
2245
ld l,(ix+block.next)
2246
ld h,(ix+block.next+1)
2247
ld (iy+block.next),l
2248
ld (iy+block.next+1),h
2257
memory.alloc.nextblock:
2258
ld l,(ix+block.next)
2259
ld h,(ix+block.next+1)
2266
;; this = this->next
2269
jp memory.alloc.find
2274
;; HL = IX - block_header_size
2281
ld ix,memory.heap_start
2283
ld e,(ix+block.next)
2284
ld d,(ix+block.next+1)
2287
jp z, memory.free.passedend
2288
sbc hl,de ; test this (HL) against next (DE)
2289
jr c, memory.free.found ; if DE > HL
2290
add hl,de ; restore hl value
2292
pop ix ; current = next
2295
;; ix=prev, hl=this, de=next
2297
add hl,de ; restore hl value
2298
ld (ix+block.next), l
2299
ld (ix+block.next+1), h ; prev->next = this
2302
ld (iy+block.next), e
2303
ld (iy+block.next+1), d ; this->next = next
2304
push ix ; prev x this
2309
call memory.free.coalesce
2310
pop ix ; this x next
2311
jr memory.free.coalesce
2315
memory.free.coalesce:
2316
ld c, (iy+block.size)
2317
ld b, (iy+block.size+1) ; bc = this->size
2321
adc hl, bc ; hl = this + this->size
2325
sbc hl, de ; if this + this->size == next, then this->size += next->size, this->next = next->next
2326
jr z, memory.free.coalesce.do
2327
push ix ; else, new *this = *next
2330
memory.free.coalesce.do:
2331
ld l, (ix+block.size)
2332
ld h, (ix+block.size+1) ; hl = next->size
2334
adc hl, bc ; hl += this->size
2335
ld (iy+block.size), l
2336
ld (iy+block.size+1), h ; this->size = hl
2337
ld l, (ix+block.next)
2338
ld h, (ix+block.next+1) ; hl = next->next
2339
ld (iy+block.next), l
2340
ld (iy+block.next+1), h ; this->next = hl
2343
memory.free.passedend:
2344
;; append block at the end of the free list
2345
ld (ix+block.next),l
2346
ld (ix+block.next+1),h
2349
ld (iy+block.next),0
2350
ld (iy+block.next+1),0
2356
ld ix,memory.heap_start
2358
memory.get_free.count:
2360
add a,(ix+block.size)
2363
adc a,(ix+block.size+1)
2365
ld l,(ix+block.next)
2366
ld h,(ix+block.next+1)
2372
jr memory.get_free.count
2374
memory.error: ld e, 7 ; out of memory
2375
__call_basic BASIC_ERROR_HANDLER ;
2380
;---------------------------------------------------------------------------------------------------------
2382
;---------------------------------------------------------------------------------------------------------
2391
RET_MATH_LIB: call COPY_TO.TMP_DAC
2397
MATH_DECADD: ld ix, addSingle
2402
if defined MATH.SUB or defined MATH.NEG
2404
MATH_DECSUB: ld ix, subSingle
2409
if defined MATH.MULT
2411
MATH_DECMUL: ld ix, mulSingle
2418
MATH_DECDIV: ld ix, divSingle
2426
MATH_SNGEXP: ld ix, powSingle
2433
MATH_COS: ld ix, cosSingle
2440
MATH_SIN: ld ix, sinSingle
2447
MATH_TAN: ld ix, tanSingle
2454
MATH_ATN: ld ix, atanSingle
2461
MATH_SQR: ld ix, sqrtSingle
2468
MATH_LOG: ld ix, lnSingle
2475
MATH_EXP: ld ix, expSingle
2482
MATH_ABSFN: ld ix, absSingle
2487
if defined MATH.SEED or defined MATH.NEG
2489
MATH_NEG: ld ix, negSingle
2496
MATH_SGN: ld ix, sgnSingle
2501
if defined RND or defined MATH.SEED
2503
MATH_RND: ld ix, randSingle
2508
MATH_FRCINT: ld hl, BASIC_DAC
2521
ld (BASIC_VALTYP), a
2524
MATH_FRCDBL: ; same as MATH_FRCSGL
2525
MATH_FRCSGL: ld hl, BASIC_DAC+2 ; input address
2526
ld bc, BASIC_DAC ; output address
2529
ld (BASIC_VALTYP), a
2532
MATH_ICOMP: ld a, h ; cp hl, de (alternative to bios DCOMPR)
2534
jr nz, MATH_ICOMP.NE.HIGH
2537
jr nz, MATH_ICOMP.NE.LOW
2539
MATH_ICOMP.NE.HIGH: jr c, MATH_ICOMP.GT.HIGH
2541
jr nz, MATH_DCOMP.GT
2543
MATH_ICOMP.GT.HIGH: bit 7, d
2546
MATH_ICOMP.NE.LOW: jr c, MATH_DCOMP.GT
2549
MATH_XDCOMP: ; same as MATH_DCOMP
2550
MATH_DCOMP: ld ix, cmpSingle
2554
MATH_DCOMP.GT: ld a, 0xFF ; DAC > ARG
2556
MATH_DCOMP.EQ: ld a, 0 ; DAC = ARG
2558
MATH_DCOMP.LT: ld a, 1 ; DAC < ARG
2561
if defined CAST_STR_TO.VAL
2563
MATH_FIN: ; HL has the source string
2564
ld a, (BASIC_VALTYP)
2565
cp 2 ; test if integer
2567
ld hl, (BASIC_DAC+2)
2572
MATH_FIN.1: ld BC, BASIC_DAC
2578
if defined CAST_INT_TO.STR
2580
MATH_FOUT: ld a, (BASIC_VALTYP)
2581
cp 2 ; test if integer
2583
ld hl, (BASIC_DAC+2)
2588
MATH_FOUT.1: ld hl, BASIC_DAC
2599
;---------------------------------------------------------------------------------------------------------
2601
; Copyright 2018 Zeda A.K. Thomas
2602
;---------------------------------------------------------------------------------------------------------
2604
; https://github.com/Zeda/z80float
2605
; https://www.omnimaga.org/asm-language/(z80)-floating-point-routines/
2606
; https://en.wikipedia.org/wiki/Single-precision_floating-point_format
2607
;---------------------------------------------------------------------------------------------------------
2609
; HL points to the first operand
2610
; DE points to the second operand (if needed)
2611
; IX points to the third operand (if needed, rare)
2612
; BC points to where the result should be output
2613
; Floats are stored by a little-endian 24-bit mantissa. However, the highest bit
2614
; is taken as implicitly 1, so we replace it as a sign bit. Next comes an 8-bit
2615
; exponent biased by +128.
2616
;---------------------------------------------------------------------------------------------------------
2617
; Adapted to MSXBas2Asm by Amaury Carvalho, 2019
2618
;---------------------------------------------------------------------------------------------------------
2620
;---------------------------------------------------------------------------------------------------------
2622
;---------------------------------------------------------------------------------------------------------
2624
BASIC_HOLD8: equ 0xF806 ; 48 Work area for decimal multiplications.
2625
BASIC_HOLD2: equ 0xF836 ; 8 Work area in the execution of numerical operators.
2626
BASIC_HOLD: equ 0xF83E ; 8 Work area in the execution of numerical operators.
2627
scrap: equ BASIC_HOLD8
2628
seed0: equ BASIC_RNDX
2629
seed1: equ seed0 + 4
2630
var48: equ scrap + 4
2633
addend2: equ scrap+7 ;4 bytes
2634
var_x: equ BASIC_HOLD8 + 4 ;4 bytes
2635
var_y: equ var_x + 4 ;4 bytes
2636
var_z: equ var_y + 4 ;4 bytes
2637
var_a: equ var_z + 4 ;4 bytes
2638
var_b: equ var_a + 4 ;4 bytes
2639
var_c: equ var_b + 4 ;4 bytes
2640
temp: equ var_c + 4 ;4 bytes
2641
temp1: equ temp + 4 ;4 bytes
2642
temp2: equ temp1 + 4 ;4 bytes
2643
temp3: equ temp2 + 4 ;4 bytes
2645
pow10exp_single: equ scrap+9
2646
strout_single: equ 0xF750 ; PARM2 - BASIC_BUF ;pow10exp_single+2
2648
;---------------------------------------------------------------------------------------------------------
2650
;---------------------------------------------------------------------------------------------------------
2652
;;Still need to tend to special cases
2720
pop hl ;bigger float
2852
;;Need to adjust sign flag
2875
;;How many push/pops are needed?
2883
;;How many push/pops are needed?
2889
;;How many push/pops are needed?
2890
;;Return bigger number
2897
;---------------------------------------------------------------------------------------------------------
2899
;---------------------------------------------------------------------------------------------------------
2922
jp addInject ;jumps in to the addSingle routine
2924
;---------------------------------------------------------------------------------------------------------
2926
;---------------------------------------------------------------------------------------------------------
2929
;Inputs: HL points to float1, DE points to float2, BC points to where the result is copied
2930
;Outputs: float1*float2 is stored to (BC)
2931
;573+mul24+{0,35}+{0,30}
2934
;avg: 2055.13839751681cc
2960
;;return float in CHLB
2970
jr z,mulSingle_case0
2982
;jr z,mulSingle_case1
2986
jp z,mulSingle_case1
2991
rra ; |Lots of help from Runer112 and
2992
adc a,a ; |calc84maniac for optimizing
2993
jp po,bad ; |this exponent check.
3002
call mul24 ;BDE*CHL->HLBCDE, returns sign info
3059
;special*x = special
3080
;basically, if b|c has bit 5 set, return NaN
3113
;;avg :1464.9033203125cc (1464+925/1024)
3116
;avg: 1449.63839751681cc
3157
;---------------------------------------------------------------------------------------------------------
3159
;---------------------------------------------------------------------------------------------------------
3162
;;HL points to numerator
3163
;;DE points to denominator
3164
;;BC points to where the quotient gets written
3166
divSingle_no_pushpop:
3172
xor (hl) ; |Get sign of output
3179
ex de,hl ; |Get exponent
3286
call divsub1 ;34 or 66
3304
;34cc or 66cc or 93cc
3319
;---------------------------------------------------------------------------------------------------------
3321
; https://www.geeksforgeeks.org/write-a-c-program-to-calculate-powxn/
3322
; https://stackoverflow.com/questions/3518973/floating-point-exponentiation-without-power-function
3323
;---------------------------------------------------------------------------------------------------------
3324
;double mypow( double base, double power, double precision )
3326
; if ( power < 0 ) return 1 / mypow( base, -power, precision );
3327
; else if ( power >= 1 ) return base * mypow( base, power-1, precision );
3328
; else if ( precision >= 1 ) {
3329
; if( base >= 0 ) return sqrt( base );
3330
; else return sqrt( -base );
3331
; } else return sqrt( mypow( base, power*2, precision*2 ) );
3334
if defined MATH.POW or defined MATH_EXP or defined MATH_LOG or defined MATH_LN
3340
;;BC points to output
3344
ld bc, var_y ; power
3349
ld hl, const_precision
3350
ld bc, var_a ; precision
3353
ld bc, var_z ; result
3362
; if ( power < 0 ) return 1 / mypow( base, -power, precision );
3368
; else if ( power >= 1 ) return base * mypow( base, power-1, precision );
3374
; else if ( precision >= 1 ) {
3375
; if( base >= 0 ) return sqrt( base );
3376
; else return sqrt( -base );
3382
; } else return sqrt( mypow( base, power*2, precision*2 ) );
3407
; return 1 / mypow( base, -power, precision );
3426
; return base * mypow( base, power-1, precision );
3445
; if( base >= 0 ) return sqrt( base );
3446
; else return sqrt( -base );
3472
; 2^x = 1.000000001752 + x * (0.693146989552 + x * (0.2402298085906 + x * (5.54833215071e-2 + x * (9.67907584392e-3 + x * (1.243632065103e-3 + x * 2.171671843714e-4)))))
3473
;Please note that usually I like to reduce to [-.5,.5] as the extra overhead is usually worth it.
3474
;In this case, our polynomial is the same degree, with error different by less than 1 bit, so it's just a waste to range-reduce in this way.
3477
;x-=int(x) ;leaves x in [0,1)
3479
;;if x==inf -> out==inf
3480
;;if x==-inf -> out==0
3481
;;if x==NAN -> out==NAN
3488
push af ;keep track of sign
3498
jr c,_pow_1 ;int(x)=0
3511
jr nz,exp_normalized
3522
jr exp_normalized ;.db $11 ;start of `ld de,**`
3529
jr comp_exp ;.db $06 ;start of 'ld b,*` just to eat the next byte
3538
jp z,exp_underflow+1
3539
;perform 1-(var48+10)--> var48+10
3547
;our 'x' is at var48+10
3548
;our `temp` is at var48+6 so as not to cause issues with mulSingle)
3549
;uses 14 bytes of RAM
3591
;-inf -> +0 because lim approaches 0 from the right
3613
;-inf -> +0 because lim approaches 0 from the right
3615
sbc a,a ;FF if should be 0,
3630
;---------------------------------------------------------------------------------------------------------
3632
;---------------------------------------------------------------------------------------------------------
3634
if defined MATH_SQR or defined MATH_EXP
3636
;Uses 3 bytes at scrap
3638
;552+{0,19}+8{0,3+{0,3}}+pushpop+sqrtHLIX
3657
jp z,sqrtSingle_special
3660
push af ;new exponent
3670
;AHL is the new remainder
3671
;Need to divide by 2, then divide by the 16-bit (var_x+4)
3675
;We are just going to approximate it
3757
;Output: DE is the sqrt, AHL is the remainder
3758
;speed: 754+{0,1}+6{0,6}+{0,3+{0,18}}+{0,38}+sqrtHL
3782
jr _15a ;.db $FE ;start of `cp *`
3796
jr _16a ;.db $FE ;start of `cp *`
3810
jr _17a ;.db $FE ;start of `cp *`
3824
jr _18a ;.db $FE ;start of `cp *`
3828
;Now we have four more iterations
3829
;The first two are no problem
3841
jr _19a ;.db $FE ;start of `cp *`
3855
jr _20a ;.db $FE ;start of `cp *`
3860
;On the next iteration, HL might temporarily overflow by 1 bit
3862
rl d ;sla e \ rl d \ inc e
3866
adc hl,hl ;This might overflow!
3867
jr c,sqrt32_iter15_br0
3880
;On the next iteration, HL is allowed to overflow, DE could overflow with our current routine, but it needs to be shifted right at the end, anyways
3883
ld b,a ;either 0x00 or 0x80
3904
;returns A as the sqrt, HL as the remainder, D = 0
3918
jr _23a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
3929
jr _24a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
3940
dec d ;this resets the low bit of D, so `srl d` resets carry.
3941
jr _25a ;.db $06 ;start of ld b,* which is 7cc to skip the next byte.
3963
jr _27a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
3976
jr _28a ;.db $01 ;start of ld bc,** which is 10cc to skip the next two bytes.
3998
;---------------------------------------------------------------------------------------------------------
4000
;---------------------------------------------------------------------------------------------------------
4002
if defined MATH_LOG or defined MATH_LN
4005
; x / (1 + x/(2-x+4x/(3-2x+9x/(4-3x+16x/(5-4x)))))
4006
; a * x ^ (1/a) - a, where a = 100
4009
ld de, const_100_inv
4011
call powSingle ; temp = x ^ (1/100)
4015
call mulSingle ; temp1 = temp * 100
4018
call subSingle ; bc = temp1 - 100
4023
;---------------------------------------------------------------------------------------------------------
4025
;---------------------------------------------------------------------------------------------------------
4042
;---------------------------------------------------------------------------------------------------------
4044
;---------------------------------------------------------------------------------------------------------
4051
;;BC points to the output
4056
;;DE points to lg(y), HL points to x, BC points to output
4065
;---------------------------------------------------------------------------------------------------------
4067
; https://en.wikipedia.org/wiki/List_of_trigonometric_identities
4068
; https://en.wikipedia.org/wiki/Taylor_series#Trigonometric_functions
4069
; https://cs.stackexchange.com/questions/89245/how-approximate-sine-using-taylor-series
4070
; https://stackoverflow.com/questions/42217069/approximating-sinex-with-a-taylor-series-in-c-and-having-a-lot-of-problems
4071
;---------------------------------------------------------------------------------------------------------
4073
if defined MATH_SIN or defined MATH_TAN or defined MATH_COS
4076
; taylor: x - x^3/6 + x^5/120 - x^7/5040
4077
; x(1 - x^2(1/6 - x^2(1/120 - x^2/5040)) )
4079
; var_b = round( x / (2*PI), 0 )
4080
; var_c = x - var_b*2*PI
4081
; temp1 = if( var_c >= 0, var_c, var_c + 2*PI )
4082
; temp2 = if( temp1 > PI, temp1 - PI, temp1 )
4083
; var_a = if( temp2 > PI/2, PI - temp2, temp2 ) * if( temp1 > PI, -1, 1 )
4090
call copySingle ; return 0
4094
call trigRangeReductionSinCos
4099
call mulSingle ; var_b = var_a * var_a
4103
call mulSingle ; temp = x^2/5040
4107
call subSingle ; temp1 = 1/120 - temp
4111
call mulSingle ; temp = x^2 * temp1
4115
call subSingle ; temp1 = 1/6 - temp
4119
call mulSingle ; temp = x^2 * temp1
4123
call subSingle ; temp1 = 1 - temp
4127
call mulSingle ; return x * temp1
4130
trigRangeReductionSinCos:
4133
; var_b = round( x / (2*PI), 0 )
4141
; var_c = x - var_b*2*PI
4145
call mulSingle ; temp = var_b*2*PI
4149
call subSingle ; var_c = x - temp
4150
; temp1 = if( var_c >= 0, var_c, var_c + 2*PI )
4154
jr nc, trigRangeReductionSinCos.else.2
4157
call copySingle ; temp1 = var_c
4158
jr trigRangeReductionSinCos.endif.2
4159
trigRangeReductionSinCos.else.2:
4163
call addSingle ; temp1 = var_c + 2*PI
4164
trigRangeReductionSinCos.endif.2:
4165
; temp2 = if( temp1 > PI, temp1 - PI, temp1 )
4169
jr c, trigRangeReductionSinCos.else.3
4170
jr z, trigRangeReductionSinCos.else.3
4174
call subSingle ; temp2
4175
jr trigRangeReductionSinCos.endif.3
4176
trigRangeReductionSinCos.else.3:
4179
call copySingle ; temp2 = temp1
4180
trigRangeReductionSinCos.endif.3:
4181
; var_a = if( temp2 > PI/2, PI - temp2, temp2 ) * if( temp1 > PI, -1, 1 )
4182
ld hl, const_half_pi
4185
jr c, trigRangeReductionSinCos.else.4
4186
jr z, trigRangeReductionSinCos.else.4
4190
call subSingle ; var_a
4191
jr trigRangeReductionSinCos.endif.4
4192
trigRangeReductionSinCos.else.4:
4195
call copySingle ; var_a = temp2
4196
trigRangeReductionSinCos.endif.4:
4197
; if( temp > PI, -1, 1 )
4201
jr nc, trigRangeReductionSinCos.endif.5
4205
ld (ix+2), a ; turn var_a to negative
4206
trigRangeReductionSinCos.endif.5:
4212
;---------------------------------------------------------------------------------------------------------
4214
;---------------------------------------------------------------------------------------------------------
4216
if defined MATH_COS or defined MATH_TAN
4219
; taylor: 1 - x^2/2 + x^4/24 - x^6/720
4220
; 1 - x^2(1/2 - x^2(1/24 - x^2/720) )
4221
; reduction: same as sin
4230
call copySingle ; return 1
4234
; 1 - x^2(1/2 - x^2(1/24 - x^2/720) )
4235
call trigRangeReductionSinCos
4240
call mulSingle ; var_b = var_a * var_a
4244
call mulSingle ; temp = x^2/720
4248
call subSingle ; temp1 = 1/24 - temp
4252
call mulSingle ; temp = x^2 * temp1
4256
call subSingle ; temp1 = 1/2 - temp
4260
call mulSingle ; temp = x^2 * temp1
4264
call subSingle ; temp1 = 1 - temp
4266
; temp3 = abs(var_c)
4267
; temp1 = temp1 * if( temp3 >= PI/2, -1, 1 ) ==> cos sign
4274
ld (ix+2), a ; temp3 = abs(var_c)
4276
ld de, const_half_pi
4277
call cmpSingle ; if temp3 >= PI/2 then temp1 = -temp1
4278
jr nc, cosSingle.endif.1
4282
ld (ix+2), a ; temp1 = -temp1
4286
call copySingle ; return temp1
4291
;---------------------------------------------------------------------------------------------------------
4293
;---------------------------------------------------------------------------------------------------------
4314
;---------------------------------------------------------------------------------------------------------
4316
;---------------------------------------------------------------------------------------------------------
4321
;taylor: x/(1 + x^2/(3 + (2*x)^2/(5 + (3*x)^2/(7+(4*x)^2/9) ) ) )
4322
; x < -1: atan - PI/2
4323
; x >= 1: PI/2 - atan
4324
;reduction: abs(X) > 1 : Y = 1 / X
4325
; abs(X) <= 1: Y = X
4334
call copySingle ; return 0
4338
;x/(1 + x^2/(3 + (2*x)^2/(5 + (3*x)^2/(7+(4*x)^2/9) ) ) )
4339
call trigRangeReductionAtan
4345
call mulSingle ; var_b = var_a * var_a
4349
call mulSingle ; temp = (4*x)^2
4353
call divSingle ; temp1 = temp/9
4357
call addSingle ; temp = 7 + temp1
4361
call mulSingle ; temp1 = var_b * 9
4365
call divSingle ; temp2 = temp1 / temp
4369
call addSingle ; temp = 5 + temp2
4373
call mulSingle ; temp1 = var_b * 4
4377
call divSingle ; temp2 = temp1 / temp
4381
call addSingle ; temp = 3 + temp2
4385
call divSingle ; temp2 = var_b / temp
4389
call addSingle ; temp = 1 + temp2
4393
call divSingle ; temp2 = var_a / temp
4395
; x >= 1: PI/2 - atan
4399
ld hl, const_half_pi
4406
; x < -1: atan - PI/2
4417
ld de, const_half_pi
4426
call copySingle ; return temp2
4429
trigRangeReductionAtan:
4430
;reduction: abs(X) > 1 : Y = 1 / X
4431
; abs(X) <= 1: Y = X
4440
ld (ix+2), a ; abs(x)
4444
jr nc, trigRangeReductionAtan.1
4450
jr trigRangeReductionAtan.2
4451
trigRangeReductionAtan.1:
4456
trigRangeReductionAtan.2:
4460
jr c, trigRangeReductionAtan.3
4464
ld (ix+2), a ; y = -y
4465
trigRangeReductionAtan.3:
4470
if defined MATH_SIN or defined MATH_TAN or defined MATH_COS
4472
;---------------------------------------------------------------------------------------------------------
4474
;---------------------------------------------------------------------------------------------------------
4488
;---------------------------------------------------------------------------------------------------------
4490
;---------------------------------------------------------------------------------------------------------
4559
if defined MATH_ABSFN
4561
;---------------------------------------------------------------------------------------------------------
4563
;---------------------------------------------------------------------------------------------------------
4566
;;HL points to the float
4567
;;BC points to where to output the result
4586
;---------------------------------------------------------------------------------------------------------
4588
;---------------------------------------------------------------------------------------------------------
4591
;;HL points to the float
4592
;;BC points to where to output the result
4597
if defined powSingle or defined sgnSingle or defined MATH_NEG
4599
;---------------------------------------------------------------------------------------------------------
4601
;---------------------------------------------------------------------------------------------------------
4604
;;HL points to the float
4605
;;BC points to where to output the result
4611
jr nz, negSingle.test.sign
4614
jr nz, negSingle.test.sign
4617
jr nz, negSingle.test.sign
4620
jr nz, negSingle.test.sign
4631
negSingle.test.sign:
4634
jr z, negSingle.positive
4638
call negSingle.positive
4657
if defined MATH_DCOMP or defined MATH.POW or defined MATH_EXP or defined MATH_LOG or defined MATH_LN or defined MATH_SIN or defined MATH_TAN or defined MATH_COS or defined MATH_ATN
4659
;---------------------------------------------------------------------------------------------------------
4661
;---------------------------------------------------------------------------------------------------------
4664
;Input: HL points to float1, DE points to float2
4666
; float1 >= float2 : nc
4667
; float1 < float2 : c,nz
4668
; float1 == float2 : z
4669
; There is a margin of error allowed in the lower 2 bits of the mantissa.
4671
;Currently fails when both numbers have magnitude less than about 2^-106
4706
ld a,(scrap+3) ;new power
4707
pop bc ;B is old power
4717
or 1 ;not equal, so reset z flag
4718
rla ;if negative, float1<float2, setting c flag as wanted, else nc.
4728
;---------------------------------------------------------------------------------------------------------
4730
;---------------------------------------------------------------------------------------------------------
4733
;Stores a pseudo-random number on [0,1)
4734
;it won't produce values on (0,2^-23)
4743
;DEHL is the mantissa, B is the exponent
4759
;If we needed to shift more than 8 bits, we'll load in more random data
4764
jp nc,rand_no_more_rand_data
4772
rand_no_more_rand_data:
4791
;;Tested and passes all CAcert tests
4792
;;Uses a very simple 32-bit LCG and 32-bit LFSR
4793
;;it has a period of 18,446,744,069,414,584,320
4794
;;roughly 18.4 quintillion.
4795
;;LFSR taps: 0,2,6,7 = 11000101
4797
;;Thanks to Runer112 for his help on optimizing the LCG and suggesting to try the much simpler LCG. On their own, the two are terrible, but together they are great.
4798
;Uses 64 bits of state
4834
if defined MATH_FOUT
4836
;---------------------------------------------------------------------------------------------------------
4838
; in HL = Single address
4839
; BC = String address
4840
; out A = String size
4841
; http://0x80.pl/notesen/2015-12-29-float-to-string.html
4842
; http://0x80.pl/articles/convert-float-to-integer.html
4843
;---------------------------------------------------------------------------------------------------------
4857
; Move the float to scrap
4861
; Make the float negative, write a '-' if already negative
4870
ld a,'-' ; write '-' simbol
4878
; Check if the exponent field is 0 (a special value)
4885
; We should write '0' next. When rounding 9.999999... for example, not padding with a 0 will return '.' instead of '1.'
4893
; Now we need to perform signed (A-128)*77 (approximation of exponent*log10(2))
4901
ld (pow10exp_single),a ;The base-10 exponent
4905
ld de,pow10LUT ;get the table of 10^-(2^k)
4907
ld hl, pow10exp_single
4909
call singletostr_mul
4910
call singletostr_mul
4911
call singletostr_mul
4912
call singletostr_mul
4913
call singletostr_mul
4914
call singletostr_mul
4915
;now the number is pretty close to a nice value
4917
; If it is less than 1, multiply by 10
4922
;ld hl,scrap ;Since singletostr_mul returns BC = scrap, can do this cheaper
4928
ld hl,pow10exp_single
4934
; Convert to a fixed-point number !
4948
;We need to get 7 digits
4950
pop hl ;Points to the string
4952
;The first digit can be as large as 20, so it'll actually be two digits
4956
;Increment the exponent :)
4957
ld de,(pow10exp_single-1)
4959
ld (pow10exp_single-1),de
4968
; Get the remaining digits.
4975
call singletostrmul10
4980
;Save the pointer to the end of the string
4987
jr c,rounding_done_single
4988
jr _40a ;.db $DA ;start of `jp c,*` in order to skip the next instruction
4997
rounding_done_single:
5000
;Strip the leading zero if it exists (rounding may have bumped this to `1`)
5012
;Now lets move HL-DE bytes at DE+1 to DE
5024
;If z flag is reset, this means that the exponent should be bumped up 1
5025
ld a,(pow10exp_single)
5028
ld (pow10exp_single),a
5031
;if -4<=A<=6, then need to insert the decimal place somewhere.
5036
;for this, we need to insert the decimal after the first digit
5037
;Then, we need to append the exponent string
5039
ld de,strout_single-1
5041
cp '-' ;negative sign
5049
;remove any stray zeroes at the end before appending the exponent
5053
; Write the exponent
5056
ld a,(pow10exp_single)
5059
ld (hl),'-' ;negative sign
5077
ld de, strout_single
5080
ld a, l ; string size
5082
ld hl,strout_single-1
5086
ld a,(pow10exp_single)
5090
;need to put zeroes before everything
5093
cp '-' ;negative sign
5119
ld de,strout_single-1
5123
cp '-' ;negative sign
5134
ld hl,strout_single-1
5152
;multiply the 0.24 fixed point number at scrap by 10
5153
;overflow in A register
5188
;Check that the last digit isn't a decimal!
5242
;---------------------------------------------------------------------------------------------------------
5244
; https://www.ticalc.org/pub/86/asm/source/routines/atof.asm
5245
;---------------------------------------------------------------------------------------------------------
5250
ptr_sto: equ scrap+9
5252
;;#Routines/Single Precision
5254
;; HL points to the string
5255
;; BC points to where the float is output
5257
;; scrap+9 is the pointer to the end of the string
5259
;; 11 bytes at scrap ?
5264
;Check if there is a negative sign.
5273
;Skip all leading zeroes
5276
jr z,$-4 ;jumps back to the `inc hl`
5279
;Check if the next char is char_DEC
5281
or a ;to reset the carry flag
5283
jr _54a ;.db $FE ;start of cp *
5290
jr z,$-5 ;jumps back to the `dec b`
5293
;Now we read in the next 8 digits
5299
;Now `scrap` holds the 4-digit base-100 number.
5301
;if carry flag is set, just need to get rid of remaining digits
5302
;Otherwise, need to get rid of remaining digits, while incrementing the exponent
5313
jp z,strToSingle_inf
5316
;Now check for engineering `E` to modify the exponent
5320
;Gotta multiply the number at (scrap) by 2^24
5323
call scrap_times_256
5326
call scrap_times_256
5329
call scrap_times_256
5332
call scrap_times_256
5335
;Now scrap+3 is a 4-byte mantissa that needs to be normalized
5343
jp z,strToSingle_zero-1
5347
jp m,strToSingle_normed
5348
;Will need to iterate at most three times
5361
;Move the number to scrap
5370
;now (scrap) is our number, need to multiply by power of 10!
5371
;Power of 10 is stored in B, need to put in A first
5379
jp nc,strToSingle_inf+1
5382
jp nc,strToSingle_zero
5406
cp char_NEG ;negative exponent?
5458
call scrap_times_sub
5471
jr nz,strToSingle_inf
5489
if defined roundSingle or defined MATH_FRCSGL
5491
;---------------------------------------------------------------------------------------------------------
5493
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Math:Division#24.2F8_division
5494
;---------------------------------------------------------------------------------------------------------
5501
ld l, (ix) ; convert integer parameter to single float
5503
ld bc, 0x1000 ; bynary digits count + sign
5505
int2Single.test.zero:
5507
or h ; test if hl is not zero
5508
jr nz, int2Single.test.negative
5510
jr nz, int2Single.test.negative
5515
int2Single.test.negative:
5516
bit 7, h ; test if hl is negative
5517
jr z, int2Single.normalize
5518
ld c, 0x80 ; sign negative
5527
int2Single.normalize:
5530
jr nz, int2Single.mount
5533
jr int2Single.normalize
5536
res 7, h ; turn off upper bit
5538
ld a, c ; restore sign
5540
ld h, a ; ...into upper mantissa
5542
ld e, h ; sign+mantissa
5543
ld h, l ; high mantissa
5544
ld l, 0 ; low mantissa
5546
ld a, b ; binary digits count
5547
or 0x80 ; exponent bias
5552
ld (ix), l ; low mantissa
5553
ld (ix+1), h ; high mantissa
5554
ld (ix+2), e ; sign + mantissa
5555
ld (ix+3), d ; expoent
5564
if defined roundSingle or defined MATH_FRCINT
5566
;---------------------------------------------------------------------------------------------------------
5568
; http://0x80.pl/articles/convert-float-to-integer.html
5569
;---------------------------------------------------------------------------------------------------------
5572
; HL points to the single-precision float
5574
; HL is the 16-bit signed integer part of the float
5575
; BC points to 16-bit signed integer
5592
jr c,no_shift_single_to_int16
5594
jr nc,no_shift_single_to_int16
5616
jr _67a ;.db $11 ;start of ld de,*
5628
no_shift_single_to_int16:
5650
;---------------------------------------------------------------------------------------------------------
5651
; Auxiliary routines
5652
;---------------------------------------------------------------------------------------------------------
5659
const_pi: db $DB,$0F,$49,$81
5660
const_e: db $54,$f8,$2d,$81
5661
const_lg_e: db $3b,$AA,$38,$80
5662
const_ln_2: db $18,$72,$31,$7f
5663
const_log2: db $9b,$20,$1a,$7e
5664
const_lg10: db $78,$9a,$54,$81
5665
const_0: db $00,$00,$00,$00
5666
const_1: db $00,$00,$00,$80
5667
const_2: dw 0, 33024
5668
const_3: dw 0, 33088
5669
const_4: dw 0, 33280
5670
const_5: dw 0, 33312
5671
const_7: dw 0, 33376
5672
const_9: dw 0, 33552
5673
const_16: dw 0, 33792
5674
const_100: db $00,$00,$48,$86
5675
const_100_inv: dw 55050, 31011
5676
const_precision: db $77,$CC,$2B,$65 ;10^-8
5677
const_half_1: dw 0, 32512
5678
const_inf: db $00,$00,$40,$00
5679
const_NegInf: db $00,$00,$C0,$00
5680
const_NaN: db $00,$00,$20,$00
5681
const_log10_e: db $D9,$5B,$5E,$7E
5682
const_2pi: db $DB,$0F,$49,$82
5683
const_2pi_inv: db $83,$F9,$22,$7D
5684
const_half_pi: dw 4059, 32841
5685
const_p25: db $00,$00,$00,$7E
5686
const_p5: db $00,$00,$00,$7F
5689
sin_a1: dw 43691, 32042
5690
sin_a2: dw 34952, 30984
5691
sin_a3: dw 3329, 29520
5692
cos_a1: equ const_half_1
5693
cos_a2: dw 43691, 31530
5694
cos_a3: dw 2914, 30262
5695
exp_a1: db $15,$72,$31,$7F ;.693146989552
5696
exp_a2: db $CE,$FE,$75,$7D ;.2402298085906
5697
exp_a3: db $7B,$42,$63,$7B ;.0554833215071
5698
exp_a4: db $FD,$94,$1E,$79 ;.00967907584392
5699
exp_a5: db $5E,$01,$23,$76 ;.001243632065103
5700
exp_a6: db $5F,$B7,$63,$73 ;.0002171671843714
5701
const_1p40625: db $00,$00,$34,$80 ;1.40625
5703
if defined MATH_CONSTSINGLE
5711
;A is the constant ID#
5712
;returns nc if failed, c otherwise
5713
;HL points to the constant
5714
cp (end_const-start_const)>>2
5721
;#if ((end_const-4)>>8)!=(start_const>>8)
5734
db $CD,$CC,$4C,$7C ;.1
5735
db $0A,$D7,$23,$79 ;.01
5736
db $17,$B7,$51,$72 ;.0001
5737
db $77,$CC,$2B,$65 ;10^-8
5738
db $95,$95,$66,$4A ;10^-16
5739
db $1F,$B1,$4F,$15 ;10^-32
5742
db $00,$00,$20,$83 ;10
5743
db $00,$00,$48,$86 ;100
5744
db $00,$40,$1C,$8D ;10000
5745
db $20,$BC,$3E,$9A ;10^8
5746
db $CA,$1B,$0E,$B5 ;10^16
5747
db $AE,$C5,$1D,$EA ;10^32
5754
;C>=128 135+6{0,33+{0,1}}+{0,20+{0,8}}
5755
;C>=64 115+5{0,33+{0,1}}+{0,20+{0,8}}
5756
;C>=32 95+4{0,33+{0,1}}+{0,20+{0,8}}
5757
;C>=16 75+3{0,33+{0,1}}+{0,20+{0,8}}
5758
;C>=8 55+2{0,33+{0,1}}+{0,20+{0,8}}
5759
;C>=4 35+{0,33+{0,1}}+{0,20+{0,8}}
5760
;C>=2 15+{0,20+{0,8}}
5763
;avg: 349.21279907227cc
5854
;26 bytes, adds 118cc to the traditional routine
5889
;c flag means don't increment the exponent
5892
jr c,ascii_to_uint8_noexp
5894
jr z,ascii_to_uint8_noexp-2
5898
jr nc,ascii_to_uint8_noexp_end
5910
jr z,ascii_to_uint8_noexp_2nd
5914
jr nc,ascii_to_uint8_noexp_end
5925
ascii_to_uint8_noexp:
5928
jr nc,ascii_to_uint8_noexp_end
5935
ascii_to_uint8_noexp_2nd:
5940
jr nc,ascii_to_uint8_noexp_end
5943
jr ascii_2 ;.db $FE ;start of `cp **`, saves 1cc
5944
ascii_to_uint8_noexp_end:
5954
if defined MATH_RSUBSINGLE
5975
jp addInject ;jumps in to the addSingle routine
5979
if defined MATH_MOD1SINGLE
5981
;This routine performs `x mod 1`, returning a non-negative value.
6004
jr z,mod1Single_special
6017
;If it is zero, need to set exponent to zero and return
6040
;make sure it isn't zero else we need to add 1
6052
;If INF, need to return NaN instead
6053
;For 0 and NaN, just return itself :)
6073
if defined MATH_FOUT
6075
; --------------------------------------------------------------
6076
; Converts a signed integer value to a zero-terminated ASCII
6077
; string representative of that value (using radix 10).
6079
; Brandon Wilson WikiTI
6080
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Other:DispA#Decimal_Signed_Version
6081
; --------------------------------------------------------------
6083
; HL Value to convert (two's complement integer).
6084
; DE Base address of string destination. (pointer).
6085
; --------------------------------------------------------------
6088
; --------------------------------------------------------------
6089
; REGISTERS/MEMORY DESTROYED
6091
; --------------------------------------------------------------
6097
; Detect sign of HL.
6101
; HL is negative. Output '-' to string and negate HL.
6106
; Negate HL (using two's complement)
6110
ld a, 0 ; Note that XOR A or SUB A would disturb CF
6114
; Convert HL to digit characters
6116
ld b, 0 ; B will count character length of number
6119
call div_hl_c; HL = HL / A, A = remainder
6126
; Retrieve digits from stack
6134
; Terminate string with NULL
6145
ld a, l ; string size
6153
;===============================================================
6154
; Convert a string of base-10 digits to a 16-bit value.
6155
; http://z80-heaven.wikidot.com/math#toc32
6157
; DE points to the base 10 number string in RAM.
6159
; HL is the 16-bit value of the number
6160
; DE points to the byte after the number
6165
; A (actually, add 30h and you get the ending token)
6168
; n is the number of digits
6170
; at most 595 cycles for any 16-bit decimal value
6171
;===============================================================
6174
ld hl,0 ; 10 : 210000
6191
jr nc,ConvLoop ;12|23: 30EE
6193
jr ConvLoop ; --- : 18EB
6200
; return remainder in a
6201
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Math:Division
6222
; http://wikiti.brandonw.net/index.php?title=Z80_Routines:Math:Division#24.2F8_division
6252
djnz div_dehl_c.loop
6260
;---------------------------------------------------------------------------------------------------------
6261
; VARIABLES INITIALIZE
6262
;---------------------------------------------------------------------------------------------------------
6266
ld (VAR_DUMMY.COUNTER), a ; max circular queue = 8 dummys
6267
ld hl, VAR_DUMMY.DATA ; start of variable dummy circular queue
6268
ld (VAR_DUMMY.POINTER), hl
6269
ld b, VAR_DUMMY.LENGTH
6274
djnz INITIALIZE_DUMMY.1
6279
ld (BASIC_DATPTR), hl ; next DATA pointer to use by READ command
6281
ld (BASIC_DATLIN), hl ; index of DATA item to use by READ command
6284
INITIALIZE_VARIABLES:
6285
call INITIALIZE_DATA
6286
call INITIALIZE_DUMMY
6289
call gfxInitSpriteCollisionTable
6292
;if defined COMPILE_TO_ROM
6293
; ld ix, BIOS_JIFFY ; initialize rom clock
6301
ld d, 2 ; any = default integer
6302
ld c, 0 ; variable name 1 (variable number)
6303
ld b, 0 ; variable name 2 (type flag=any)
6304
call INIT_VAR ; variable initialize
6306
ld d, 2 ; any = default integer
6307
ld c, 1 ; variable name 1 (variable number)
6308
ld b, 0 ; variable name 2 (type flag=any)
6309
call INIT_VAR ; variable initialize
6313
;---------------------------------------------------------------------------------------------------------
6314
; MAIN WORK AREA - LITERALS / VARIABLES / CONFIGURATIONS
6315
;---------------------------------------------------------------------------------------------------------
6317
if defined COMPILE_TO_ROM
6320
pgmPage1.pad: equ pageSize - (workAreaPad - pgmArea)
6322
if pgmPage1.pad >= 0
6325
; .WARNING "There's no free space left on program page 1"
6330
VAR_STACK.START: equ ramArea
6331
;VAR_STACK.END: equ VAR_STACK.START + 0x800 ; 2kb (~200 variables)
6333
VAR_STACK.POINTER: equ VAR_STACK.START
6335
PRINT.CRLF: db 3, 0, 0, 2
6336
dw PRINT.CRLF.DATA, 0, 0, 0
6337
PRINT.CRLF.DATA: db 13,10,0
6339
PRINT.TAB: db 3, 0, 0, 1
6340
dw PRINT.TAB.DATA, 0, 0, 0
6341
PRINT.TAB.DATA: db 09,0
6344
LIT_NULL_DBL: dw 0, 0, 0, 0
6350
LIT_QUOTE_CHAR: db '\"'
6353
LIT_TRUE: db 2, 0, 0
6357
LIT_FALSE: db 2, 0, 0
6362
LIT_5: db 3, 0, 0, 16
6365
LIT_5_DATA: db "<<< Counting >>>", 0
6368
IDF_6: equ VAR_STACK.POINTER + 0
6379
IDF_11: equ VAR_STACK.POINTER + 11
6390
LIT_15: db 3, 0, 0, 3
6391
dw LIT_15_DATA, 0, 0
6393
LIT_15_DATA: db " = ", 0
6399
AFTER_LAST_VARIABLE: equ VAR_STACK.POINTER + 22
6401
VAR_DUMMY.START: equ AFTER_LAST_VARIABLE ; variable dummy circular queue area
6402
VAR_DUMMY.COUNTER: equ VAR_DUMMY.START ; variable dummy circular queue count
6403
VAR_DUMMY.POINTER: equ VAR_DUMMY.COUNTER + 1 ; pointer to next variable dummy
6404
VAR_DUMMY.DATA: equ VAR_DUMMY.POINTER + 2 ; first variable dummy
6406
VAR_DUMMY.SIZE: equ 8
6407
VAR_DUMMY.LENGTH: equ (11 * VAR_DUMMY.SIZE)
6408
VAR_DUMMY.END: equ VAR_DUMMY.DATA + VAR_DUMMY.LENGTH
6409
VAR_STACK.END: equ VAR_DUMMY.END + 1
6411
;--------------------------------------------------------
6413
;--------------------------------------------------------
6416
DATA_ITEMS_COUNT: equ 0
6418
DATA_SET_ITEMS_START:
6419
DATA_SET_ITEMS_COUNT: equ 0
6422
;---------------------------------------------------------------------------------------------------------
6424
;---------------------------------------------------------------------------------------------------------
6426
if defined COMPILE_TO_ROM
6430
pgmPage2.pad: equ romSize - (romPad - pgmArea)
6432
if pgmPage2.pad >= 0
6435
if pgmPage2.pad < lowLimitSize
6436
.WARNING "There's only less than 5% free space on this ROM"
6439
.ERROR "There's no free space left on this ROM"
6444
end_file: end start_pgm ; label start is the entry point